The general principle behind most INSUFFICIENT_ACCESS_ON_CROSS_REFERENCE_ENTITY
errors is that the running user doesn't have access to a referenced (i.e. lookup) record due to Record sharing rules.
For example, the Apex transaction in question is effectively:
API: insert new Lead(fldA = ..., fldB = ..., ...);
Apex:
...
void onAfterTriggerHandler(Lead[] recs) {
Task[] tasks = new List<Task>();
for (Lead l : recs) {
tasks.add(new Task(..., WhoId = l.Id, ...);
}
insert tasks; // INSUFFICIENT_ACCESS_ON_CROSS_REFERENCE_ENTITY occurs here
}
If running User X can insert the Lead, why can't it insert a Task pointing at a Lead it just created, given that the Criteria-Based sharing rules give User X
R/W to Leads inserted with field values provided by User X
in the original API call?
And the answer is ...
- If one of the fields provided in the initial Lead insert is an
OwnerId
other than User X
or
- Before insert trigger/flow logic changes the inserted Lead's
OwnerId
to a value other than User X
Then because criteria-based sharing rules aren't applied until the after Insert
is completed, the Task.WhoId
points at the Lead owned by some other User Y
. Since the Lead's after Insert
is still in flight, the operative sharing rule is OWD Private. Thus running User X
can't yet see the Lead it just inserted as the inserted Lead referenced by WhoId
is owned by User Y
.
Your workaround depends on your application; for example, delegate the creation of the Tasks to an async transaction like future or queueable. By the time the async transaction starts, the Criteria-based sharing rules will have been applied by SFDC and the Lead will be visible to User X
, regardless of its ownership.