INVALID_CROSS_REFERENCE_KEY : Invalid cross reference id in After insert Trigger to create ContentDocumentLink

apexcontentdocumentcontentdocumentlinktrigger

I am encountering the following error when inserting a ContentDocumentLink record :

Error [statusCode=INVALID_CROSS_REFERENCE_KEY, code=[xmlrpc=1202, statusCode=INVALID_CROSS_REFERENCE_KEY, exceptionCode=INVALID_CROSS_REFERENCE_KEY, scope=PublicApi, http=400]

The insert operation is in an After Insert trigger on the object ContentDocument, I am trying to link the newly created ContentDocument with an existing Account. I already checked the following options :

  • Checked that the Account Id in the LinkedEntityId fiel, and the ContentDocument Id in the ContentDocumentId field of ContentDocumentLink record are valid Salesforce Ids;
  • The file is correctly uploaded and visible in Salesforce;
  • I also tried with Ids in static strings of the existing Account and an already existing ContentDocument, and it didn't throw this error. I know that it is possible to create a ContentDocumentLink when a ContentDocument is inserted, since I already implemented it in another project but I no longer have access to the code.

Any tips on what I am missing would be greatly appreciated.
Thank you.

EDIT #1 : Trigger Handler code

List<ContentDocumentLink> doclinksToCreate = new List<ContentDocumentLink>();
   Map<String, String> docIdsToAccountIds = new Map<String, String>();
    Map<String, String> userIdsToDocIds = new Map<String, String>();
    for(ContentDocument oneDoc : docs){
        userIdsToDocIds.put(oneDoc.CreatedById, oneDoc.Id);
    }

    // Get a list of AccountIds related to portal users that created ContentDocuments
    List<User> portalUsers = [SELECT Id, AccountId, Account.Name, Profile.Name 
                            FROM User 
                            WHERE Id IN :userIdsToDocIds.keySet()
                            AND Profile.Name = :System.Label.TheProfileName];

    Set<String> accountIds = new Set<String>();
    for(User oneUser : portalUsers){
        docIdsToAccountIds.put(userIdsToDocIds.get(oneUser.Id), oneUser.AccountId);
    }

    for(String key : docIdsToAccountIds.keySet()){
        doclinksToCreate.add(new ContentDocumentLink(
            ContentDocumentId = key, //'0693M000000fhOcQAI'
            LinkedEntityId = docIdsToAccountIds.get(key), // '0013M000018llyNQAQ'
            ShareType = 'I',
            Visibility = 'AllUsers' 
        ));
    }

    Database.SaveResult[] results = Database.insert(doclinksToCreate, false);
    Database.SaveResult oneRes;
    List<String> accountsIdsToNotifyAbout = new List<String>();
    for(Integer i=0, size = results.size(); i<size; i++){
        oneRes = results[i];
        if(oneRes.isSuccess()){
            System.debug('Insert document link success for document with Id : '+doclinksToCreate[i].ContentDocumentId);
            accountsIdsToNotifyAbout.add(doclinksToCreate[i].LinkedEntityId);
        }else{
            System.debug('Error with document Id : '+doclinksToCreate[i].ContentDocumentId);
            for(Database.Error oneError : oneres.getErrors()){
                System.debug(oneError);
            }
        }
    }

Best Answer

I ended up changing the logic behind the ContentDocumentLink creation in order to solve the problem. I wrote a trigger on after insert of a ContentVersion instead of a ContentDocument, then I query for the ContentDocumentId.

It had nothing to do with certain fields being null or access to apex classes and to objects. I guess the issue comes from the ContentDocument record not being committed to the database yet in an after insert trigger (Salesforce order of execution) even if the record Id is available.