Salesforce Dictionary - Free Salesforce GlossarySalesforce Dictionary
All errors
Validation

FIELD_CUSTOM_VALIDATION_EXCEPTION: <your validation rule's error message>

A validation rule on the object evaluated to TRUE for the record being saved, blocking the save. The text after the colon is the rule's own error message — it's the message your admin wrote for end users.

Also seen asFIELD_CUSTOM_VALIDATION_EXCEPTION·FIELD_CUSTOM_VALIDATION·validation rule error

A sales rep clicks Save on an opportunity. The page bounces back with a red banner: FIELD_CUSTOM_VALIDATION_EXCEPTION: Opportunity amount must be greater than zero when stage is Closed Won. The rep can't proceed. The opportunity has a stage of Closed Won and an Amount of $0. The admin who wrote that validation rule intended to block exactly this case. The system is working as designed; the rep is mad anyway.

What the platform is doing

A validation rule is a formula attached to an object that evaluates on every save. If the formula returns TRUE, the platform blocks the save and surfaces the rule's error message to the user. FIELD_CUSTOM_VALIDATION_EXCEPTION is the API-side error code; the part after the colon is the message your admin wrote.

The check happens for every record save: manual edits in the UI, Apex DML, REST/SOAP API calls, data loader imports, Flow record updates. Anywhere a record gets written, validation rules run. The only exceptions:

  • Before triggers can prevent the save themselves by addError(), which fires before validation rules.
  • Workflow field updates can bypass validation rules if the corresponding setting is unchecked in the rule's metadata.
  • System-managed updates (the platform updating its own audit fields) skip validation rules.

For an Apex developer, the error usually surfaces as a DmlException inside the save call. The exception's getMessage() returns the rule's error text; getDmlType(0) returns FIELD_CUSTOM_VALIDATION_EXCEPTION; getDmlFields(0) returns the field the rule named (if any).

The broken example

A bulk import script that updates opportunities, blocked by a validation rule the script's author didn't know existed:

public class OpportunityBulkUpdate {
    public static void closeOpportunities(List<Id> opportunityIds, String reason) {
        List<Opportunity> opps = [
            SELECT Id, StageName, CloseDate, Amount
            FROM Opportunity
            WHERE Id IN :opportunityIds
        ];
        for (Opportunity o : opps) {
            o.StageName = 'Closed Won';
            o.CloseDate = Date.today();
            o.Close_Reason__c = reason;
            // Doesn't set Amount; if the rule requires Amount > 0 on Closed Won,
            // any opportunity with Amount = 0 (or null) will fail.
        }
        update opps;
    }
}

The script runs as System Administrator. The Apex doesn't reference validation rules. The save still fails because the validation rule fires server-side and blocks any record where the stage is Closed Won and the Amount is zero.

The fix: cooperate with the rule, don't bypass it

The fix usually isn't to disable the validation rule. It's to make the data satisfy the rule before saving. Three patterns work.

Set the required field upstream. If the rule requires Amount when stage is Closed Won, the script should set or refuse to set Closed Won when Amount is missing. Skipping the rule produces bad data; satisfying it keeps the data consistent.

Use partial-success DML to log and continue. If some records will legitimately fail the rule (because they're missing data the script can't fill in), use Database.update(records, false) to let the batch continue past individual failures. Log the failures for an admin to follow up.

Refactor the script's responsibility. A script that updates a heterogeneous set of records often violates a rule on some. Split the script into per-state paths that each respect the rule.

The fixed example, with partial-success and validation cooperation:

public class OpportunityBulkUpdate {
    public static void closeOpportunities(List<Id> opportunityIds, String reason) {
        List<Opportunity> opps = [
            SELECT Id, StageName, CloseDate, Amount
            FROM Opportunity
            WHERE Id IN :opportunityIds
        ];

        List<Opportunity> toUpdate = new List<Opportunity>();
        List<Opportunity> blocked = new List<Opportunity>();

        for (Opportunity o : opps) {
            if (o.Amount == null || o.Amount <= 0) {
                blocked.add(o);
                continue;
            }
            o.StageName = 'Closed Won';
            o.CloseDate = Date.today();
            o.Close_Reason__c = reason;
            toUpdate.add(o);
        }

        if (!blocked.isEmpty()) {
            System.debug(LoggingLevel.WARN,
                'Skipped ' + blocked.size() + ' opportunities without an Amount. '
                + 'Manual review required: ' + blocked);
            // Optionally write to a custom logging object so admins can review.
        }

        if (!toUpdate.isEmpty()) {
            Database.SaveResult[] results = Database.update(toUpdate, false);
            for (Integer i = 0; i < results.size(); i++) {
                if (!results[i].isSuccess()) {
                    System.debug(LoggingLevel.ERROR,
                        'Opportunity ' + toUpdate[i].Id + ' failed: '
                        + results[i].getErrors());
                }
            }
        }
    }
}

The script now respects the validation rule: opportunities without an Amount don't get pushed to Closed Won. The partial-success DML insulates the batch from individual record-level failures. The diagnostic logs name what went wrong without rolling back successful work.

When to bypass the rule (rarely)

Sometimes a bulk operation genuinely should bypass a validation rule. The classic case: a data migration that imports historical data, where the validation rule's intent (preventing future bad data) shouldn't apply to historical records.

The platform doesn't let Apex skip validation rules at the API level. The workarounds:

Disable the rule temporarily. Set the rule's Active flag to false, run the migration, then re-enable. Use a deploy script or the metadata API for an idempotent toggle. The risk: while the rule is disabled, normal users can also save bad data.

Use Custom Metadata to gate the rule. Wrap the rule's formula in IF($CustomMetadata.Bypass_Rules__mdt.YourRuleName.Active__c, FALSE, ...). Toggle the metadata record to disable the rule for users with a specific permission. The migration script runs as a user who has the bypass permission; the rule effectively becomes a no-op for them.

Use the Apex Sharing settings. Apex code that runs without sharing and uses certain elevated-privilege patterns can in some platform contexts skip validation rules. This is an edge case and not officially supported; treat as last-resort.

For most cases, fix the data before save instead of bypassing the rule. The rule exists for a reason.

The error from the Apex side

When update opps triggers a validation rule failure, the Apex DmlException carries detailed information:

try {
    update opp;
} catch (DmlException ex) {
    for (Integer i = 0; i < ex.getNumDml(); i++) {
        System.debug(
            'Record: ' + ex.getDmlId(i)
            + '; Status code: ' + ex.getDmlType(i)
            + '; Fields: ' + ex.getDmlFields(i)
            + '; Message: ' + ex.getDmlMessage(i)
        );
    }
}

For a single-record DML, getDmlMessage(0) returns the rule's error text. The text is exactly what the admin wrote when creating the rule, so it's user-facing and usually meaningful.

If the rule's error is on a specific field, the validation rule has a Error Location setting that names the field. The getDmlFields(0) call returns the named field. This is what lets the UI render the error inline near the problematic field instead of as a top-of-page banner.

A discipline for admins writing rules

Some validation-rule habits make life better for everyone downstream:

Write user-facing messages. "Amount must be greater than zero when stage is Closed Won" is helpful. "Validation failed" is not. The user sees this message; make it actionable.

Reference the right field in Error Location. Inline errors at the field beat banner errors at the top. Set the rule's Error Location to the specific field whenever possible.

Document the rule in the description. The Description field on a validation rule isn't visible to users, but it is visible to other admins. Explain why the rule exists, not just what it does. Future admins maintaining the org will thank you.

Audit rules quarterly. Rules that block a small percentage of saves are working as intended. Rules that block 30% of saves are too strict or too out-of-touch with how users actually work. Spot-check the validation-rule statistics in Setup → Object Manager → object → Validation Rules.

Bypassing rules in tests

Test code sometimes needs to insert "impossible" records to verify other behavior. The pattern of Custom Metadata bypass mentioned above is the right path: a metadata flag that test setup turns on, the rule's formula respects, tests insert their fixture data, then the flag turns off. Tests run in their own transaction and don't affect production behavior.

A common anti-pattern is using Test.isRunningTest() inside the validation rule formula. The platform supports this via $User.UserType or other context, but it's brittle and lets test-only code paths drift from production. Custom Metadata is cleaner.

When the rule's error message is misleading

Sometimes a rule's error message lies. The rule fires on a condition that's coincidentally true but not the actual intent. For example:

Validation rule: AND(StageName = 'Closed Won', Amount = 0)

Error message: "Amount must be greater than zero when stage is Closed Won"

The rule also fires when Amount is null (because Amount = 0 is true when Amount is null in some formula contexts).

The user sees "Amount must be greater than zero" but the actual blocker is that Amount is null. The user fixes the wrong thing and resaves.

Audit your rule formulas for null-handling. ISBLANK(Amount) || Amount = 0 is clearer than Amount = 0 and gives a more useful diagnostic when the rule fires.

Validation rules versus required fields

A required field at the field-definition level (Setup → Object Manager → object → Fields → required checkbox) and a validation rule that enforces non-null are similar but not identical:

  • A required field blocks save at the field level, with the platform's stock "Required field missing" message.
  • A validation rule with ISBLANK(Field) blocks save at the record level, with your custom message.

The required-field check is faster and produces a clearer inline error. The validation rule gives you more control over the message and lets you combine multiple conditions.

For "this field is required when X is also true," validation rules are the only option. For "this field is required always," the field-level required flag is cheaper and clearer.

Interaction with Flow and Process Builder

A record-triggered flow that updates a record can trigger validation rules. If the flow's update path violates a rule, the entire transaction (the original save plus the flow's downstream updates) rolls back. The user sees the rule's error, attributed to the original save.

This is a common surprise: the user edited field A, the flow updated field B to a value that violates a rule, and the user gets blamed for the validation failure. The diagnostic is to look for record-triggered flows on the object and audit their assignments.

For high-traffic flows, set the flow's "Bypass validation rules" option to TRUE on the field updates, where appropriate. But use this sparingly: the bypass exists to handle specific bookkeeping cases, not to dodge legitimate rules.

Custom errors via Apex addError

Apex triggers can simulate validation rules via record.addError(message):

trigger OpportunityTrigger on Opportunity (before insert, before update) {
    for (Opportunity o : Trigger.new) {
        if (o.StageName == 'Closed Won' && (o.Amount == null || o.Amount <= 0)) {
            o.Amount.addError('Amount must be greater than zero when stage is Closed Won');
        }
    }
}

This produces a FIELD_CUSTOM_VALIDATION_EXCEPTION-like response at the API level. The advantage over a validation rule formula: full Apex logic, including SOQL lookups, can drive the condition. The disadvantage: the logic is in code instead of declarative config, which makes it harder for admins to audit.

Use Apex addError when the validation logic can't be expressed in a formula. Use a validation rule otherwise. The decision rule is "can this be expressed in a formula?" If yes, use a validation rule.

Further reading from Salesforce

Related dictionary terms

Share this fix

Share on LinkedInShare on X

Related Validation errors