We ran into a strange issue today. We have an Apex trigger that is calling an Apex service class. The service class was using with sharing and in our testing, users were able to create records even though they were not granted write access via security profile to that object (object level security is read only).

However, today those users are no longer able to create records on those same objects. But, if we change with sharing to without sharing, those users are once again able to create records for objects they do not have write permissions on based on security profile.

It's my understanding with sharing enforces row-level access and has nothing to do with OLS or FLS. However, based on our observation, by applying with sharing, Salesforce enforces OLS and by applying without sharing, Salesforce does not enforce OLS.

I understand a trigger calling an Apex class will run in System context and bypass OLS/FLS, and by using with sharing one should be able to pull records shared with that user, as described in a similar answer:


However, the second paragraph in this answer explicitly states:

It is a surprisingly common misconception that with sharing enforces FLS and CRUD, which is why you may have seen this claimed elsewhere. It does not.

Although, what we have observed today is the opposite of this.

For reference the error we received is as follows:

System.DmlException: Insert failed. First exception on row 0; first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, MyFancyTrigger: execution of AfterInsert caused by: System.DmlException: Upsert failed. First exception on row 0 with id ACDC123; first error: INSUFFICIENT_ACCESS_OR_READONLY, insufficient access rights on object id: [ABC123]: []

The object referenced by record with Id ABC123 is a custom object the user only has READ only access to per security profile. The stack trace leads to a line where the code is trying to insert a record into this custom object.

The custom object is a child record in a Master-Detail relationship to Opportunity, which is currently set as Public Read Only in org wide sharing. Related records have a minimum of Read access. What happens is when the account owner is transferred (by the user who receives the error), the user transfers the opportunities and the action of changing the account owner triggers the Apex to create related master-detail records in the custom object, for which the user has read-only access.

As I write this out, there does appear to be some complexities around changing account owners, opportunity owners, and creating detail records for those opportunities whose owner just changed all in the same transaction.

It's worth noting that we do not want the user to manage these records via the UI, so we want them to remain READ ONLY.

Does anyone know why this might be? Perhaps there is some kind of update to Salesforce that changes this behavior?

  • 2
    Could you please edit the question adding the error message?
    – RubenDG
    Commented Aug 26, 2022 at 18:26
  • 3
    Does user have READ access to the specific records including lookup records? This is controlled by record row visibility. Do you change ownership on the inserted record away from the user?
    – cropredy
    Commented Aug 26, 2022 at 18:39
  • @cropredy great questions. I updated the question to include that detail. Commented Aug 26, 2022 at 19:00
  • 1
    these kind of in-flight ownership changes might be better handled via an async operation (future/queueable/async flow path running in system context w/o sharing)
    – cropredy
    Commented Aug 26, 2022 at 19:02
  • Since the error states "Upsert failed" does the issue happen only when the record already exist (update) or also when the record is new (create)?
    – RubenDG
    Commented Aug 27, 2022 at 5:27


You must log in to answer this question.

Browse other questions tagged .