[SalesForce] How to troubleshoot Exception thrown when using WITH SECURITY_ENFORCED

I've added the WITH SECURITY_ENFORCED clause to my SOQL queries. I'm testing them with a user who has a permission set that grants all object permissions (read, create, etc.), and also has read and edit rights to all fields. I'm getting the System.QueryException: Insufficient permissions: secure query included inaccessible field error. The user in question is a community user, so I've given External Users Public read access to the object as well. I'm stumped on what could be causing this issue. How do I troubleshoot, to understand what the clause is complaining about?

UPDATE:

I'm trying out the SObjectAccessDecision test recommended below. For both of the objects included in my query, the results returned by the securityDecision are null.

SObjectAccessDecision securityDecision = 
            Security.stripInaccessible(AccessType.READABLE,
                 [SELECT Id, Name
                    , Logo_Height__c
                    , Logo_Width__c
                    , Logo_URL__c
                    , Import_Font__c
                    , Font_Family__c
                    , Storefront__c
                    , Custom_CSS__c
                    , CC_Account_Group__c
                    , For_Anonymous_Users__c
                    , Use_Header_Logo_in_Footer__c
                    , Footer_Logo_Height__c
                    , Footer_Logo_URL__c
                    , Footer_Logo_Width__c
                    , Header_Logo_Display_Preference__c
                    , Favicon_Url__c
                    , Override_Logo_Size_Unit__c
                    , Footer_Override_Logo_Size_Unit__c
                    , (
                    SELECT Id, Name, Background_Color__c, Text_Color__c, Text_Decoration__c, Text_Font_Weight__c, Link_State__c, Type__c, Text_Size__c, Border__c FROM Quick_Theme_Elements__r
                    WHERE Active__c = TRUE
            )
            FROM Quick_Theme__c
            WHERE Active__c = TRUE
            AND CC_Account_Group__c = :accountGroupId
            AND Storefront__c = :storefront
            //WITH SECURITY_ENFORCED 
            ORDER BY LastModifiedDate DESC]
                 );

            // Get the removed field names - these are the fields they don't have read access to.
            System.debug(securityDecision.getRemovedFields().get('Quick_Theme__c')); // put sobject here
            System.debug(securityDecision.getRemovedFields().get('Quick_Theme_Element__c')); // put sobject here

Best Answer

I'd recommend using the stripInaccessible method to figure out exactly what field isn't accessible.

Change your query to not use WITH SECURITY ENFORCED and pass the query into the stripInaccessible method. You will get a SObjectAccessDecision that contains the removed field names. (code below mostly taken from documentation).

SObjectAccessDecision securityDecision = 
         Security.stripInaccessible(AccessType.READABLE,
                 [SELECT Name, BudgetedCost, ActualCost FROM Campaign]
                 );

// Get the removed field names - these are the fields they don't have read access to.
System.debug(securityDecision.getRemovedFields().get('Campaign')); // put sobject here

Edit: In Summer 20 notes, there's a mention that you can use stripInaccessible method to enforce field and object-level security for relationship fields. This seems to imply it didn't work before on relationships (or perhaps was unpredictable) which may explain why it might not have worked for you.

enter image description here