Salesforce Dictionary - Free Salesforce GlossarySalesforce Dictionary
All errors
Validation

DUPLICATE_VALUE: duplicate value found

You tried to insert or update a record with a value on a unique field (External ID set to "Unique" or one of the unique standard fields like User.Username) that already exists on another record. The fix is either to use a different value, upsert via the External ID, or merge the duplicate.

Also seen asDUPLICATE_VALUE·duplicate value found·DUPLICATE_VALUE: duplicate value found·duplicate value on record

A nightly integration syncs accounts from a customer's CRM into Salesforce. It's been running cleanly for months. This morning the log shows fifty failed inserts with DUPLICATE_VALUE: duplicate value found: <unknown> duplicates value on record with id: <unknown>. The records were brand new. The CRM has no concept of Salesforce ids. There shouldn't be any duplicates to clash against.

What the platform is actually checking

DUPLICATE_VALUE fires when a DML operation tries to write a value that violates a uniqueness constraint already declared on the target field. Three configurations create uniqueness constraints in Salesforce:

The first is the External ID + Unique combination on a custom field. When you mark a custom field as both External ID and Unique, the platform enforces that no two records on the same object can share the same value. An upsert keyed on that field uses it as the lookup; an insert that re-uses an existing value fails.

The second is the Unique attribute on standard fields like Username on User. The platform-defined uniqueness constraint is non-removable; you cannot ship two users with the same username.

The third is duplicate management rules configured in Setup. These are softer: a rule can be Block (fail the DML) or Allow (warn but proceed). Block rules surface as DUPLICATE_VALUE or DUPLICATES_DETECTED depending on the configuration.

The message duplicate value found: <unknown> duplicates value on record with id: <unknown> is the platform telling you it found a clash but isn't surfacing the field name or the record id. This usually means the duplicate management rule matched on a fuzzy criterion (a matching rule that uses approximate-equality on Name, for example) and the platform doesn't have a single field to name.

The broken example

A daily bulk load that upserts accounts keyed on an external CRM id:

public class AccountSyncBatch implements Database.Batchable<SObject> {
    public Iterable<SObject> start(Database.BatchableContext bc) {
        return ExternalSystem.fetchAccountsToSync();
    }

    public void execute(Database.BatchableContext bc, List<SObject> scope) {
        List<Account> toUpsert = new List<Account>();
        for (SObject row : scope) {
            Account a = new Account(
                Name = (String) row.get('name'),
                CRM_External_Id__c = (String) row.get('crm_id'),
                Industry = (String) row.get('industry'),
                Type = (String) row.get('type')
            );
            toUpsert.add(a);
        }
        upsert toUpsert CRM_External_Id__c;
    }

    public void finish(Database.BatchableContext bc) {}
}

CRM_External_Id__c is marked External ID + Unique on Account. The upsert uses it as the match key. The batch ran cleanly for months.

This week, an admin enabled a new Duplicate Rule that flags accounts as duplicates when their Name is similar to an existing account using the standard "Account: Account Name + Billing Address" matching rule. The rule's action is set to Block. Fifty of the new accounts being inserted have names similar enough to existing accounts to trigger the rule.

The upsert fails on those rows with DUPLICATE_VALUE. The integration's error handler doesn't distinguish between the External ID uniqueness violation and the Duplicate Rule violation; both surface as the same error code. The integration logs the failure and moves on. The fifty accounts are silently missing from Salesforce.

A second shape: an integration that writes Contact records and uses Email as the unique key by virtue of a custom field Email_External_Id__c mapped from Email. Two contacts in the source system share an email address (one personal, one corporate). The first insert succeeds. The second fails with DUPLICATE_VALUE. The integration didn't anticipate that the source could contain duplicates on what was supposed to be a unique key.

A third shape: a User insert where the username generated from a template like firstname.lastname@acme.com.prod collides with an existing user (two people named John Smith). The insert fails with DUPLICATE_VALUE on the Username field. The recruiter onboarding flow stalls.

The fix: identify which uniqueness constraint fired and which field

The first step is to figure out where the constraint lives. The message often lacks the field name, so the diagnostic is manual.

Check the object's custom fields in Setup, Object Manager, your object, Fields & Relationships. Look for the External ID + Unique markers. Note every field that has both attributes set.

Then check Setup, Duplicate Rules. Look for active rules on the same object. Each rule references a matching rule that defines the criterion. Read both to understand exactly what counts as a duplicate.

Finally, check the standard fields. Username on User. Tax ID, NPI, account number, and similar regulated identifiers often have built-in uniqueness even on standard objects.

Once you know which constraint fired, the fix depends on the constraint type.

For a custom field uniqueness violation, the duplicate value already exists in Salesforce. Query for it:

List<Account> existing = [
    SELECT Id, Name, CRM_External_Id__c
    FROM Account
    WHERE CRM_External_Id__c = :incomingCrmId
];

Now you can decide what to do. Update the existing record instead of inserting a new one. Reject the new record as invalid. Generate a different external id. The right answer depends on the business meaning of the duplicate.

For a duplicate rule violation, the rule's match criterion is fuzzy. The platform considered the incoming record a duplicate of one or more existing records based on name similarity, address similarity, or whatever the matching rule defines.

You can opt out of the rule for a specific insert via the DMLOptions flag:

Database.DMLOptions dml = new Database.DMLOptions();
dml.DuplicateRuleHeader.AllowSave = true;
Database.SaveResult sr = Database.insert(account, dml);

AllowSave = true bypasses the duplicate rule for this DML. Use it only when the source system is authoritative and the duplicate rule is meant for UI-level prevention, not bulk integrations.

The fixed example

The same batch, with duplicate-rule bypass for integrations and explicit conflict resolution for the External ID clash:

public class AccountSyncBatch implements Database.Batchable<SObject> {
    public Iterable<SObject> start(Database.BatchableContext bc) {
        return ExternalSystem.fetchAccountsToSync();
    }

    public void execute(Database.BatchableContext bc, List<SObject> scope) {
        List<Account> toUpsert = new List<Account>();
        for (SObject row : scope) {
            toUpsert.add(new Account(
                Name = (String) row.get('name'),
                CRM_External_Id__c = (String) row.get('crm_id'),
                Industry = (String) row.get('industry'),
                Type = (String) row.get('type')
            ));
        }

        Database.DMLOptions dml = new Database.DMLOptions();
        dml.DuplicateRuleHeader.AllowSave = true;
        dml.AllowFieldTruncation = false;

        Schema.SObjectField externalIdField = Account.CRM_External_Id__c;
        List<Database.UpsertResult> results = Database.upsert(
            toUpsert, externalIdField, false, dml
        );

        List<Account_Sync_Error__c> errors = new List<Account_Sync_Error__c>();
        for (Integer i = 0; i < results.size(); i++) {
            if (!results[i].isSuccess()) {
                Account a = toUpsert[i];
                for (Database.Error err : results[i].getErrors()) {
                    errors.add(new Account_Sync_Error__c(
                        CRM_Id__c = a.CRM_External_Id__c,
                        Error_Code__c = String.valueOf(err.getStatusCode()),
                        Error_Message__c = err.getMessage()
                    ));
                }
            }
        }
        if (!errors.isEmpty()) insert errors;
    }

    public void finish(Database.BatchableContext bc) {}
}

The integration now bypasses the duplicate rule (which is intended for UI usage), runs as a partial-success upsert, and logs every failure to a custom error object for human review. The fifty silently-missing accounts now surface in the error log with the underlying message visible.

Three judgment calls

When to bypass the duplicate rule. Bypass when the source system is authoritative and the rule is intended to prevent UI-level mistakes, not to govern integrations. Don't bypass when the rule exists specifically to catch integration-introduced duplicates; in that case the integration needs to consult Salesforce before writing.

When the duplicate is really a merge. Sometimes the right answer to a duplicate is to merge the existing record with the new one. The merge DML statement on Account, Contact, and Lead does this. The new data goes into the master record and the duplicate is deleted with all its child records re-parented.

When the source has its own duplicates. A clean integration deduplicates on the source side before sending. If the source genuinely contains two contacts with the same email, the integration has to either pick one as canonical, merge them on the way in, or write both with a fingerprint that distinguishes them (Contact.External_Id__c instead of Email as the unique key).

Closely related errors

ErrorCause
DUPLICATE_VALUE: duplicate value foundUniqueness constraint or duplicate rule violated
DUPLICATES_DETECTEDDuplicate rule with Block action matched
FIELD_INTEGRITY_EXCEPTIONField-level constraint violated (length, type)
INSUFFICIENT_ACCESS_OR_READONLYRead-only field, often confused with uniqueness

DUPLICATES_DETECTED is the modern name for a duplicate-rule block; older code paths still surface DUPLICATE_VALUE. Treat both as the same family.

Investigating with the Duplicate Record Set object

When a duplicate rule with Allow action fires, Salesforce records the match in a Duplicate Record Set with linked Duplicate Record Items. Query the recent activity:

SELECT Id, DuplicateRule, RecordCount, CreatedDate
FROM DuplicateRecordSet
WHERE CreatedDate = LAST_N_DAYS:7
ORDER BY CreatedDate DESC

Each Set contains two or more Duplicate Record Items pointing at the records the rule considered duplicates. This gives you a forensic view of which records were flagged and which rule flagged them.

For Block actions, the rule prevents the DML so no Duplicate Record Set is created. The forensic trail comes from the integration's error log.

Testing duplicate rules without firing them

A test class that inserts a record under a duplicate rule needs to either disable the rule for the test or accept the rule's behavior. Disabling for a single insert uses the same DMLOptions flag:

@isTest
static void canInsertAccountThatLooksLikeDuplicate() {
    Account existing = new Account(Name = 'Acme Corp', BillingCity = 'San Francisco');
    insert existing;

    Account newOne = new Account(Name = 'Acme Corp', BillingCity = 'San Francisco');
    Database.DMLOptions dml = new Database.DMLOptions();
    dml.DuplicateRuleHeader.AllowSave = true;

    Test.startTest();
    Database.SaveResult sr = Database.insert(newOne, dml);
    Test.stopTest();

    System.assert(sr.isSuccess());
}

The test confirms the bypass works and documents the expected behavior.

When the duplicate value is empty

A subtle case worth flagging: a custom field marked External ID + Unique still treats an empty string as a value. If two records both have an empty CRM_External_Id__c, the second insert fails with DUPLICATE_VALUE even though "no value" doesn't feel like a duplicate.

The platform actually distinguishes null from empty string. Null values don't trigger the uniqueness check; empty strings do. An integration that sometimes sends '' and sometimes sends null for the same logical "no value" will see intermittent failures.

The fix is to normalize the input. Convert empty strings to null before the DML:

String crmId = (String) row.get('crm_id');
if (String.isBlank(crmId)) {
    crmId = null;
}

String.isBlank returns true for both null and the empty string, and assigning null afterwards forces the platform to treat the absence consistently. The integration stops failing on records that legitimately have no external id yet.

Defensive habits

Document every uniqueness constraint in writing. Custom fields with External ID + Unique. Active duplicate rules. Standard-field constraints. The list lives somewhere your integration team can read.

Distinguish integration DML from UI DML. Bulk loads usually want partial-success and rule-bypass. UI inserts usually want the rules to fire. Build wrapper methods that pick the right DMLOptions for each context.

Capture every failure to a structured error log. Silent integration failures are the worst kind, and uniqueness violations are easy to swallow if no one looks for them.

Further reading from Salesforce

Related dictionary terms

Share this fix

Share on LinkedInShare on X

Related Validation errors