INVALID_FIELD_FOR_INSERT_UPDATE
You tried to set a field that the platform doesn't let you set in this context — usually a formula field, a system-managed field (CreatedDate, LastModifiedDate, IsDeleted), or a field that's read-only on a particular create-vs-update path.
Also seen asINVALID_FIELD_FOR_INSERT_UPDATE·Cannot specify Id in an update call·Unable to create/update fields
A short list of fields that look like ordinary fields but reject DML, with the reason for each:
| Field type | Why it rejects writes |
|---|---|
| Formula fields | Computed on read; they have no underlying storage |
| Roll-up summaries | Same — derived from child data |
| Cross-object formulas | Read at query time |
CreatedDate, CreatedById | Set by the platform at insert; immutable on update |
LastModifiedDate, LastModifiedById | Set by the platform on every save |
IsDeleted | Toggled by delete / undelete, not by update |
RecordType (the related sObject) | You set RecordTypeId, not the relationship |
| Audit fields with the Create Audit Fields permission off | Even CreatedDate becomes writable with that org-wide permission, but only on insert |
The Apex pattern that triggers this
// Cloning a record? Don't carry the audit fields across.
Account clone = original.clone(false, true, false, false);
clone.CreatedDate = original.CreatedDate; // throws on insert without the permission
clone.LastModifiedById = UserInfo.getUserId(); // throws
insert clone; // INVALID_FIELD_FOR_INSERT_UPDATE
Use clone(preserveId, deep, preserveReadOnlyTimestamps, preserveAutoNumbers) carefully. The third argument keeps audit fields and requires the "Set Audit Fields upon Record Creation" profile permission. If that permission isn't granted, the platform rejects the insert.
A subtler case: write-once fields
Some fields are set on insert and then become read-only:
Opportunity.IsClosed,Opportunity.IsWon— derived from the Stage; you can't write them directly.Case.ClosedDate— set when status moves to a closed status; not directly writable.- Anything tagged "Calculated" in the field setup screen.
If you really need to override these, the trick is usually to set the underlying field (Stage, Status) and let the platform compute the derived one.
When the message names a field you didn't even reference
If the bracketed field is one you never assigned to, two suspects:
- Your DML target is a
Map<Id, SObject>.values()and one of the cloned fields carries an audit-field shadow. Project to a fresh object that only has the columns you intend to write. - A trigger upstream is doing
Trigger.new[i].LastModifiedById = ...and bubbling up. Check the trigger.
