TL;DR
- The error means the user lacks access to a related record. It is not about the record being saved, which is why investigating that record finds nothing wrong.
- It fires when you set a lookup or master-detail field to a record the user cannot see or, for master-detail, cannot edit.
- This is deliberate. If Salesforce allowed the reference, the user could confirm a record exists and infer its content from related data, which is a disclosure through the back door.
- The API form of the message,
insufficient access rights on cross-reference id, contains the id of the record that actually failed. That id is the answer, and people routinely overlook it. - Common in integrations, guest user contexts and Apex running
with sharing, because those run as identities with far less access than the admin who tested the feature. - Granting Modify All Data makes it disappear and is almost always the wrong fix. It is a sharing model finding, not a permission shortage.
What You'll Learn
- Why the error names the record you saved rather than the one at fault
- How to extract the actual failing record id from the message
- The four causes, in the order worth checking them
- Why it appears in integrations and guest contexts but not in your testing
- The proportionate fixes, and why the broad grant is a trap
The Problem
A user reports they cannot save a Contact. You open the Contact, check their profile, check field-level security on every field, check the record's sharing, and find nothing. They have edit access. The record is not locked. Everything looks correct.
Nothing is wrong with the Contact. The problem is the Account it points to.
INSUFFICIENT_ACCESS_ON_CROSS_REFERENCE_ENTITY is thrown when a save requires access to a different record from the one being written, and the user does not have it. The error surfaces against the record you were editing, because that is where the save failed, but the access failure is on the other end of a relationship.
That mismatch between where the error appears and where the cause lives is the entire difficulty of this error. Everything else about it is straightforward.
Common questions this article answers:
- Why does the user have access to the record but still cannot save it?
- Which record is actually causing the failure?
- Why does it happen for the integration user and not for me?
Quick Answer
The error means the running user lacks sufficient access to a record referenced by the one being saved, typically through a lookup or master-detail field. Setting a lookup to a record the user cannot read is rejected, and for master-detail the user generally needs edit access to the parent, because writing a child changes the parent's roll-ups and sharing. The API version of the message, insufficient access rights on cross-reference id, includes the id of the offending record, so extract that id, look it up, and you have found the record whose access is actually the problem. The four causes worth checking in order are: sharing on the related record, the running identity being different from the one you tested with, field-level security on the lookup field itself, and a lookup filter or validation that references a record the user cannot reach. The fix is to grant access to the specific related record through the sharing model, not to grant Modify All Data, which resolves the symptom by removing the control that produced it.
Finding the record that actually failed
Start here, because it converts a search into a lookup.
Through the API and in most integration logs the message reads insufficient access rights on cross-reference id: 001xx000003DGb2. That id is not decoration. It is the record the running user could not access.
Paste it into your org after /lightning/r/ or query it:
SELECT Id, Name, OwnerId, CreatedDate FROM Account WHERE Id = '001xx000003DGb2'
The three-character prefix tells you the object before you even query, so 001 is Account, 003 is Contact, 500 is Case. Once you know the record, the question becomes the ordinary one: why can this user not see it?
In the Salesforce UI the id is frequently not surfaced, which is why UI reports of this error are harder. In that case reproduce it through the API, or check the debug log, where the full message including the id appears.
The four causes, in order
1. Sharing on the related record. The most common by far. The related record is private, owned by someone in another branch of the role hierarchy, and no sharing rule reaches the user. They can see the Contact fine; they cannot see its Account.
Check the related record's owner, the org-wide default for that object, and whether any sharing rule, role hierarchy path or manual share grants access. Salesforce's own sharing button on the record is the fastest way to see the effective answer.
2. The running identity is not who you think. This is why the error appears in production and not in your testing. The code path runs as an integration user, a guest user, a site user, or a scheduled job's context, and those identities have far less access than the admin who built the feature.
Apex declared with sharing enforces the running user's sharing, and since Summer '26 an omitted sharing declaration defaults to with sharing in newer API versions, which means code that used to run unrestricted may now enforce sharing. If this error appeared after an API version bump, that is a strong candidate, and we covered the change in the Summer '26 secure-by-default post.
3. Master-detail needs more than read. For a lookup, the user generally needs read access to the target. For master-detail, writing a child changes the parent's roll-up summaries and inherits its sharing, so the platform generally requires edit access on the parent. A user with read-only access to an Account can therefore fail to create a child record under it, which surprises people who checked that the user could see the parent and stopped there.
4. Field-level security and lookup filters. Less common and worth ruling out last. If the user cannot edit the lookup field itself, or a lookup filter restricts valid targets to records the user cannot reach, the save fails in a way that reads identically.
Why this is a sharing finding, not a permission shortage
The reason this error exists is worth understanding, because it changes what you consider an acceptable fix.
If Salesforce allowed a user to set a lookup to a record they cannot read, that user could learn things they are not entitled to. They could confirm a record exists by referencing it successfully. They could infer its content from roll-ups, related lists and reports that traverse the relationship. Blocking the reference closes a disclosure path that has nothing to do with the field they were editing.
So when this error fires, the platform is telling you something true about your access model: this user is being asked to work with data they are not entitled to see. The right question is which of those two facts is wrong. Either the user should have access to that related record, in which case fix the sharing, or the process should not be asking them to reference it, in which case fix the process.
That framing is what makes the broad grant a trap. Assigning Modify All Data does make the error disappear, and it does so by giving the user access to every record on the object, which is the maximal solution to a specific problem. It is the same failure of proportionality we describe in what developers actually need instead of Modify All Data, and it converts a narrow gap into a permanent, org-wide one that will be inherited by everyone assigned that profile or permission set afterwards.
The proportionate fixes
Matched to the cause:
A sharing rule that grants the user or their role access to the specific related records they legitimately need. This is the correct fix in most cases and the narrowest.
An owner change or role hierarchy adjustment, where the real issue is that the related record sits somewhere the user's role should reach and does not.
A permission set granting access to that object, where the requirement is genuinely object-wide and the user's job needs it. Assign it to the individuals who need it rather than widening a profile, which we argue for in deployable permission sets for delivery teams.
Changing the process so the reference is not required. Sometimes the honest answer is that a user in this role should not be linking to that record at all, and the automation asking them to is the defect.
For integrations specifically, give the integration user a purpose-built permission set covering exactly the objects it touches. Integration users are where this error concentrates, because they are usually created with minimal access and then asked to write records that reference half the data model.
Frequently Asked Questions
Q: The user can see the record they are editing. Why does it fail?
A: Because the failure is on a different record. The save writes a lookup or master-detail field pointing at something else, and that is where the access check fails. The error reports the record you were saving, which is where the save stopped, not where the problem is.
Q: How do I find which record is actually causing it?
A: Read the full API message. insufficient access rights on cross-reference id: 001xx... contains the id of the failing record, and the three-character prefix tells you the object. If you only have the UI error, reproduce it through the API or check the debug log, where the id appears.
Q: Why does our integration hit this when we do not?
A: Because the integration runs as a different user with far less access. You are almost certainly testing as an administrator, who bypasses the sharing model entirely. Test as the integration user, or use the API with its credentials, and the error reproduces immediately.
Q: We granted Modify All Data and it went away. Is that fine?
A: It works and it is disproportionate. Modify All Data grants access to every record on the object, permanently, to everyone holding that profile or permission set, in order to fix access to the specific records this process touches. It also removes a control that was doing something useful. Prefer a sharing rule or a targeted permission set.
Q: Does master-detail really need edit on the parent?
A: Generally yes, because writing a child affects the parent's roll-up summaries and the child inherits the parent's sharing. Read access to the parent is often not enough to create or update a child under it, which catches people who verified the user could see the parent and assumed that was sufficient.
Key Takeaways
- The error names the wrong record. The access failure is on a related record, not the one being saved.
- The id is in the API message.
cross-reference id: 001xx...is the answer, and the prefix tells you the object. - It concentrates in integrations, guest users and
with sharingApex, because those identities have much less access than whoever tested the feature. - Master-detail usually needs edit on the parent, not merely read.
- It is a sharing model finding. Either the user should have access to that record or the process should not require it, and both are real answers.
- Modify All Data is the trap. It fixes a narrow gap with an org-wide grant that outlives the problem.
What's Next?
Recommended Reading:
- What developers actually need instead of Modify All Data
- Deployable permission sets for delivery teams
- Summer '26: the SAML retirement and Apex secure-by-default changes
- UNABLE_TO_LOCK_ROW: the error that is really about your data
Action Items:
- Extract the id from the full API message and look it up. That single step usually ends the investigation.
- Reproduce as the actual running identity, not as an administrator, since admins bypass sharing and will never see the failure.
- Fix it with a sharing rule or a targeted permission set, and record why that access is needed, so the next reviewer understands the grant.
Responses
Checking your session.
Loading responses.