I've been trying to track down why a ContentDocumentLink trigger is not running in a specific case. In the working cases we have code that inserts ContentDocumentLink records, but in the failing case the code does this instead:

ContentVersion cv = ...;
cv.put('FirstPublishLocationId', parentId);

The documentation says about FirstPublishLocationId:

Accepts all record IDs supported by ContentDocumentLink (anything a file can be attached to, like records and groups).

Setting FirstPublishLocationId allows you to create a file and share it with an initial record/group in a single transaction, and have the option to create more links to share the file with other records or groups later. When a file is created, it’s automatically linked to the record, and PublishStatus will change to Public from Pending/Personal.

but makes no mention of trigger consequences.

Before I spend time refactoring this, can anyone confirm that this approach is why the trigger does not run?

1 Answer 1


With this ContentDocumentLink trigger handler:

@TestVisible private static Integer callCount = 0;

// Entry point from trigger
public static void handle(
        TriggerOperation op,
        Map<Id, ContentDocumentLink> oldMap,
        Map<Id, ContentDocumentLink> newMap) {


this test passes:

private static void insertFile(Id parentId) {

    ContentVersion file = new ContentVersion(
        PathOnClient = 'File - ' + parentId + '.txt', 
        FirstPublishLocationId = parentId, 
        Title = 'File Title ' + parentId, 
        VersionData = Blob.valueOf('File Body ' + parentId)
    insert file;

static void checkTriggerIsCalled() {

    Account a = new Account(Name = 'Acme');
    insert a;

    System.assertEquals(0, ContentDocumentLinkHandler.callCount);
    System.assertEquals(1, ContentDocumentLinkHandler.callCount);

so this creation pattern is NOT the cause of my problem.

The only other reason I can think of for my trigger not running is e.g. a governor limit being hit in some other ContentDocumentLink related trigger before my trigger is reached. Any other ideas?


I did not succeed in finding the cause of this so have implemented a work-around where a ContentVersion trigger is also used to track the insert with this class used in the logic:

private class Link {

    Id entityId;
    Id contentId;
    Integer hashCode;

    /** Supports ContentDocumentLink and ContentVersion. */
    Link(SObject sob) {
        if (sob.getSObjectType() == ContentDocumentLink.SObjectType) {
            ContentDocumentLink cdl = (ContentDocumentLink) sob;
            entityId = cdl.LinkedEntityId;
            contentId = cdl.ContentDocumentId;
        } else if (sob.getSObjectType() == ContentVersion.SObjectType) {
            ContentVersion cv = (ContentVersion) sob;
            entityId = cv.FirstPublishLocationId;
            contentId = cv.ContentDocumentId;

    public Boolean equals(Object o) {
        Link that = (Link) o;
        return this.entityId == that.entityId
                && this.contentId == that.contentId;

    public Integer hashCode() {
        if (hashCode == null) {
            hashCode = 0;
            if (entityId != null) hashCode += System.hashCode(entityId);
            if (contentId != null) hashCode += System.hashCode(contentId) * 31;
        return hashCode;

with duplicates blocked via a set of these objects.


See System.hashCode still broken for Id for the cause of a bug in the above class and how to fix it.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .