ContentPublishing Notification triggers twice

Hello everyone,

I’ve noticed that the ContentPublishingNotification in Umbraco is being triggered twice when publishing content. This causes my INotificationHandler<ContentPublishingNotification> to execute twice, which leads to unintended behavior in my logic.

I would like to understand:

  1. Why does ContentPublishingNotification trigger twice during a single publish action?
  2. Is there a way to prevent this behavior so that my NotificationHandler only runs once per publish event?

Here is my current handler implementation:

public class AuditLogCreatedNotificationHandler : INotificationAsyncHandler<ContentPublishingNotification>
{
    public AuditLogCreatedNotificationHandler()
    {

    }

    public async Task HandleAsync(ContentPublishingNotification notification, CancellationToken cancellationToken)
    {
       // business logic
    }
}

Has anyone else experienced this issue? Is there a recommended way to ensure the notification is only triggered once?

Thanks in advance for any insights!

1 Like

I too have the exact same questions as you!

Did you ever figure this out?
From what I can tell, the entities affected, the target, and every datapoint within the notification are identical in both instances of the notification, so I see no reason why it should happen twice.

i think thisbehavior is expected in Umbraco and is not a bug.

ContentPublishingNotification is raised as part of the publishing pipeline, not strictly once per UI “Publish” click. During a publish operation Umbraco may internally:

  • Process multiple cultures/variants

  • Run validation and publishing passes

  • Publish multiple entities in a single request

Because of this, the same content can pass through the pipeline more than once, causing the notification handler to execute multiple times.

i suggest to de-duplicate inside the handler, usually by tracking the content Id + VersionId, which uniquely identifies a publish operation.

eg: public async Task HandleAsync(
ContentPublishingNotification notification,
CancellationToken cancellationToken)
{
foreach (var content in notification.PublishedEntities)
{
var key = $“{content.Id}:{content.VersionId}”;

    // Skip if this content version was already handled
    if (AlreadyHandled(key))
        continue;

    MarkAsHandled(key);

    // Business logic
}

}