Salesforce Dictionary - Free Salesforce GlossarySalesforce Dictionary
All errors
Validation

REQUIRED_FIELD_MISSING: Required fields are missing: [FieldName]

You tried to create or update a record without populating a field that the object requires. The error message lists the exact API names in brackets — those are what you need to populate before retry.

Also seen asREQUIRED_FIELD_MISSING·Required fields are missing·REQUIRED_FIELD_MISSING: Required fields are missing

You hit Save on an opportunity and the platform throws it back: REQUIRED_FIELD_MISSING: Required fields are missing: [LastName, Email__c]. The bracketed list names exactly the API names of the fields you forgot. The fix should be simple, but the developer who wrote the migration script doesn't know how the field became required and the admin who made it required can't find which automation depends on it staying that way.

What the platform is checking

Every field has a "required" property defined in two layers.

Universally required at the field level. Setup → Object Manager → object → Fields → field → Required checkbox. If checked, every record must have a non-null value for the field on every save. The platform validates this server-side, no exceptions.

Required at the page-layout level. A field can be required on a specific page layout (Setup → Page Layouts → Edit) without being globally required. This affects UI saves through that layout, not API or Apex DML.

REQUIRED_FIELD_MISSING fires when the universal-required check fails. The error includes the API names of every missing field in brackets, so the diagnostic is straightforward: populate those fields and retry.

The broken example

A bulk-create script that omits fields the calling developer didn't realize were required:

public class LeadImporter {
    public static void importFromCsv(List<Map<String, String>> rows) {
        List<Lead> leads = new List<Lead>();
        for (Map<String, String> row : rows) {
            leads.add(new Lead(
                FirstName = row.get('firstName'),
                Company = row.get('company')
                // LastName and Email__c omitted; both are required
            ));
        }
        insert leads;
    }
}

The Lead object always requires LastName (it's a Salesforce-defined system requirement). The custom Email__c field was made required by an admin in 2023 to support a marketing automation. The script's author didn't know. The insert fails on the first row.

The fix: discover required fields dynamically

DescribeFieldResult.isNillable() returns false for required fields. The complementary isCreateable() filters to fields you're allowed to set on insert. Together, they tell you what's required and writable.

public static void importFromCsv(List<Map<String, String>> rows) {
    Set<String> requiredFields = new Set<String>();
    Map<String, Schema.SObjectField> fieldMap = Lead.SObjectType.getDescribe().fields.getMap();
    for (String fieldName : fieldMap.keySet()) {
        Schema.DescribeFieldResult dfr = fieldMap.get(fieldName).getDescribe();
        if (!dfr.isNillable() && dfr.isCreateable() && !dfr.isDefaultedOnCreate()) {
            requiredFields.add(fieldName);
        }
    }

    List<Lead> leads = new List<Lead>();
    List<String> errors = new List<String>();
    for (Integer i = 0; i < rows.size(); i++) {
        Map<String, String> row = rows[i];
        Set<String> missing = new Set<String>(requiredFields);
        missing.removeAll(row.keySet());
        if (!missing.isEmpty()) {
            errors.add('Row ' + i + ' missing: ' + missing);
            continue;
        }
        leads.add(buildLead(row));
    }

    if (!errors.isEmpty()) System.debug(LoggingLevel.WARN, errors);
    if (!leads.isEmpty()) Database.insert(leads, false);
}

The check at validation time catches the problem before DML, with a clear per-row diagnostic. Bulk imports get useful "row 17 missing LastName" messages instead of a generic transaction-level rollback.

The fixed example, end to end

A complete import service with structured error reporting:

public class LeadImporter {
    public class Result {
        @AuraEnabled public Integer rowNumber;
        @AuraEnabled public Boolean success;
        @AuraEnabled public String message;
        @AuraEnabled public Id leadId;
    }

    public static List<Result> importFromCsv(List<Map<String, String>> rows) {
        Set<String> required = computeRequiredFields(Lead.SObjectType);
        List<Lead> toInsert = new List<Lead>();
        List<Result> results = new List<Result>();
        Map<Integer, Integer> rowToInsertIdx = new Map<Integer, Integer>();

        for (Integer i = 0; i < rows.size(); i++) {
            Result r = new Result();
            r.rowNumber = i;
            Map<String, String> row = rows[i];

            Set<String> missing = new Set<String>(required);
            missing.removeAll(row.keySet());
            for (String k : new List<String>(row.keySet())) {
                if (String.isBlank(row.get(k))) missing.add(k);
            }
            missing.retainAll(required);

            if (!missing.isEmpty()) {
                r.success = false;
                r.message = 'Missing required fields: ' + missing;
                results.add(r);
                continue;
            }

            Lead lead = buildLead(row);
            rowToInsertIdx.put(i, toInsert.size());
            toInsert.add(lead);
            results.add(r);
        }

        if (toInsert.isEmpty()) return results;

        Database.SaveResult[] saveResults = Database.insert(toInsert, false);
        for (Integer rowNum : rowToInsertIdx.keySet()) {
            Integer idx = rowToInsertIdx.get(rowNum);
            Database.SaveResult sr = saveResults[idx];
            Result r = results[rowNum];
            if (sr.isSuccess()) {
                r.success = true;
                r.leadId = sr.getId();
                r.message = 'Created';
            } else {
                r.success = false;
                r.message = 'DML failed: ' + sr.getErrors();
            }
        }
        return results;
    }

    private static Set<String> computeRequiredFields(Schema.SObjectType t) {
        Set<String> required = new Set<String>();
        Map<String, Schema.SObjectField> fieldMap = t.getDescribe().fields.getMap();
        for (String fieldName : fieldMap.keySet()) {
            Schema.DescribeFieldResult dfr = fieldMap.get(fieldName).getDescribe();
            if (!dfr.isNillable()
                && dfr.isCreateable()
                && !dfr.isDefaultedOnCreate()
                && dfr.getType() != Schema.DisplayType.REFERENCE) {
                required.add(fieldName);
            }
        }
        return required;
    }

    private static Lead buildLead(Map<String, String> row) {
        return new Lead(
            FirstName = row.get('FirstName'),
            LastName = row.get('LastName'),
            Company = row.get('Company'),
            Email = row.get('Email')
        );
    }
}

The validation runs upfront, so individual missing-field failures don't poison the batch. The result object names which rows succeeded and which need work.

Two layers of "required" that surprise people

The same field can be:

  1. Required at field-definition level → blocks every save, API or UI.
  2. Required at page-layout level only → blocks save via that layout, not via API.
  3. Required by a validation rule → fires a different error (FIELD_CUSTOM_VALIDATION_EXCEPTION) with the rule's text.
  4. Required via Universally Required Field on a managed object → same as #1.

Apex bulk inserts only respect #1 and #3. Bulk imports via Data Loader or the API similarly ignore #2 (the page layout's required setting is UI-only).

If a record saves fine via API but fails in the UI with "Field is required," the field is required at the layout level. If it fails via API too, it's required globally (or has a validation rule).

The interaction with record types

Record types can change which fields are visible and required on the page layout, but they don't change field-level required at the schema level. A field that's globally required is required on every record type.

Conversely, a field that's required on Record Type A's layout but not Record Type B's is layout-only required. API saves work for both record types without setting the field.

When the validation comes from a flow or process

Sometimes a record-triggered flow sets a default value on a required field as a "make sure it's not null" safety net. If the flow doesn't run (because the trigger context excludes it, or it fails before reaching the field-set), the save fails with REQUIRED_FIELD_MISSING.

The diagnostic: check whether the field has a default-set automation. If yes, audit when that automation runs and whether your save path triggers it.

Required lookups need extra care

A required Lookup field (Master-Detail relationships are always required) fails differently when the parent id is invalid. The error might be REQUIRED_FIELD_MISSING (id is null), INVALID_CROSS_REFERENCE_KEY (id is non-null but doesn't exist), or INSUFFICIENT_ACCESS_OR_READONLY (id is valid but the user can't access it). The fixes are correspondingly different.

For bulk inserts that include lookups, validate the lookup ids exist before save:

Set<Id> parentIds = new Set<Id>();
for (Map<String, String> row : rows) {
    String parentRaw = row.get('ParentId');
    if (!String.isBlank(parentRaw)) parentIds.add((Id) parentRaw);
}
Set<Id> existingIds = new Map<Id, Account>(
    [SELECT Id FROM Account WHERE Id IN :parentIds]
).keySet();
// Now validate each row's ParentId against existingIds.

This catches the lookup-validity issue at row-validation time, before DML, with a useful diagnostic.

Test patterns

A test that verifies required-field validation:

@isTest
static void importFromCsv_reportsMissingRequiredFields() {
    List<Map<String, String>> rows = new List<Map<String, String>>{
        new Map<String, String>{
            'FirstName' => 'Alice',
            'Company' => 'Acme'
            // LastName missing on purpose
        }
    };
    Test.startTest();
    List<LeadImporter.Result> results = LeadImporter.importFromCsv(rows);
    Test.stopTest();
    System.assertEquals(1, results.size());
    System.assert(!results[0].success);
    System.assert(results[0].message.contains('LastName'));
}

The assertion confirms the import catches the missing field instead of throwing. Pairs well with a happy-path test that provides every required field and verifies success.

Bulk imports without describe-call overhead

The dynamic describe call has a small per-call cost. For very high-volume imports, cache the required-field set once and reuse:

private static Map<Schema.SObjectType, Set<String>> requiredCache = new Map<Schema.SObjectType, Set<String>>();

public static Set<String> getRequiredFields(Schema.SObjectType t) {
    if (requiredCache.containsKey(t)) return requiredCache.get(t);
    Set<String> required = computeRequiredFields(t);
    requiredCache.put(t, required);
    return required;
}

The cache survives for the transaction's life. Subsequent calls return from memory rather than re-running describe.

A common diagnostic surprise

The platform's error message names every missing field at once: [FirstName, LastName, Email]. The temptation is to fix each field one at a time, redeploying after each, and burning thirty minutes on what should be a single fix.

Read the whole list first. Apply the change once. The error doesn't trickle out one field at a time; the platform aggregates the full list of missing requirements per record. Use the aggregation to make a single change.

When the requirement was added recently

If your code worked yesterday and fails today with REQUIRED_FIELD_MISSING, an admin made a field required in production overnight. Audit the field history:

  1. Setup → Object Manager → object → Fields → field name → History.
  2. Look for an entry showing "Required: false → true."
  3. The entry includes the admin who made the change and the timestamp.

Reach out to that admin to understand whether the requirement is intentional and what the right value should be for your code's path. Often the requirement was a fix for a different problem and there's a reasonable default your code can use.

Multi-row response patterns

For bulk-insert APIs that you expose to external callers, design the response to communicate per-row outcomes. The pattern in the fixed example above (per-row Result objects with success, message, and id fields) scales well for any size of batch.

When the external caller is non-Salesforce, JSON-serialize the response with the same per-row structure. Callers can iterate the response array and act on per-row failures without needing to parse error strings.

Auditing required fields at deployment time

A discipline that prevents REQUIRED_FIELD_MISSING regression: include a metadata-level audit in CI. Parse every .object-meta.xml and .field-meta.xml file in the repo and compare the required flag against the previous version. Flag any newly-required field for human review.

The audit catches schema-level changes that admins make in production without going through code review. If the change is intentional, code can be updated to match. If accidental, the change can be rolled back before downstream code starts failing.

The interaction with workflow defaults

A workflow rule with a Field Update action can populate a required field automatically. The save runs the trigger, then the workflow, then the workflow's field update, then re-saves. If the workflow fills in the required field, the save succeeds despite the original missing value.

This is sometimes a source of confusion: code that omits a required field works "fine" because a workflow rescues it. The dependency is invisible from the code, fragile (the workflow can be deactivated by an admin), and discoverable only by tracing the automation chain.

Better practice: have the code set the required value explicitly. The workflow can still run, but it's a secondary safety net rather than the primary mechanism. Code that explicitly handles every required field is more robust to automation changes.

Custom workflow for "this field is required only in some scenarios"

Field-level required is global: every record needs the value. If you want "required only when X is true," use a validation rule instead:

Rule: Industry_Required_For_Enterprise
Condition: AND(ISBLANK(Industry), Annual_Revenue__c > 10000000)
Error: 'Industry is required for accounts with revenue over $10M.'

The validation rule fires FIELD_CUSTOM_VALIDATION_EXCEPTION instead of REQUIRED_FIELD_MISSING, but the user-facing semantic is the same. The advantage is conditional behavior; the disadvantage is the formula complexity.

For the cleanest experience, document the validation rules that act as conditional requirements alongside the universally-required fields. Both lists belong in your object's onboarding doc for new developers.

Distinguishing "required" from "should-be-populated"

A culture distinction worth establishing on your team: not every field that "should" be filled needs to be schema-required. Schema-required means the platform refuses the save without it. For fields that are best-effort or fill-eventually, schema-required is too strict and produces friction.

Three patterns for "should be populated but not blocking save":

  • Soft validation: a Lightning component shows a warning if the field is blank, without blocking.
  • Triggered reminders: a Process or Flow sends an email when a record is saved with the field blank.
  • Reporting dashboards: a report identifies records with missing fields so an admin can clean up periodically.

Apply schema-required only when the field is genuinely required for the record to be meaningful. Apply the softer patterns for fields that are "should-have" but not "must-have."

A practical onboarding habit

When a new developer joins the team, run them through one exercise: pick the object their first feature will touch, open Setup → Object Manager → object → Fields, and list every field where Required = true. Five minutes of work. The list becomes their mental model for what every save needs to include.

Pair the exercise with a second look at validation rules on the same object. Now they know both layers of "what stops a save." Cuts down on first-feature REQUIRED_FIELD_MISSING incidents to near zero.

Further reading from Salesforce

Related dictionary terms

Share this fix

Share on LinkedInShare on X

Related Validation errors