Skip to content

Push publishing archived HTML pages with multiple language versions fails on receiver #36051

Description

@jcastro-dotcms

Reported via Enterprise Support:

Problem Statement

When push-publishing an archived HTML Page that has multiple language versions (e.g., English and Spanish), the entire push publishing bundle fails on the receiver with a DotDataException: Contentlet is currently marked as 'Archived'. This affects any customer who push-publishes content that includes archived multi-language HTML pages — a situation that commonly arises when push-publishing an entire Site, since all content (including archived pages) is bundled automatically.

The root cause is in ESContentletAPIImpl.updateTemplateInAllLanguageVersions. When multiple language versions of the same archived page are processed sequentially by ContentHandler, the first language version is successfully saved and then archived on the receiver. When the second language version is processed, updateTemplateInAllLanguageVersions searches for non-archived versions of that same identifier, finds none, then searches again with includeArchived=true, finds the first language version (now archived on the receiver), and throws a DotDataException — aborting the entire push.

Example Stack trace:

dotcms-receiver-1      | 17:46:12.106  INFO  util.ActivityLogger - class com.dotcms.content.elasticsearch.business.ESContentletAPIImpl: demo.dotcms.com : Contentlet to be saved:  , StartDate: N/D; EndDate: N/D; User:system; ContentIdentifier: 902f839a441232cd7e278788c098b42f
dotcms-receiver-1      | 17:46:12.108  WARN  business.ESContentFactoryImpl - Contentlet with ID '902f839a441232cd7e278788c098b42f' exists, but is marked as 'Archived'.
dotcms-receiver-1      | 17:46:12.109  ERROR business.ESContentletAPIImpl - Contentlet is currently marked as 'Archived'.
dotcms-receiver-1      | com.dotmarketing.exception.DotDataException: Contentlet is currently marked as 'Archived'.
dotcms-receiver-1      | 	at com.dotcms.content.elasticsearch.business.ESContentletAPIImpl.updateTemplateInAllLanguageVersions(ESContentletAPIImpl.java:6809) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.content.elasticsearch.business.ESContentletAPIImpl.internalCheckin(ESContentletAPIImpl.java:5767) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.content.elasticsearch.business.ESContentletAPIImpl.lambda$checkin$63(ESContentletAPIImpl.java:5301) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.concurrent.lock.StripedLockImpl.tryLock(StripedLockImpl.java:100) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.concurrent.lock.StripedLockImpl.tryLock(StripedLockImpl.java:55) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.concurrent.lock.IdentifierStripedLock.tryLock(IdentifierStripedLock.java:16) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.content.elasticsearch.business.ESContentletAPIImpl.checkin(ESContentletAPIImpl.java:5300) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.content.elasticsearch.business.ESContentletAPIImpl.checkin(ESContentletAPIImpl.java:10395) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.content.elasticsearch.business.ESContentletAPIImpl.checkin(ESContentletAPIImpl.java:5142) ~[?:?]
dotcms-receiver-1      | 	at com.dotmarketing.portlets.contentlet.business.ContentletAPIInterceptor.checkin(ContentletAPIInterceptor.java:286) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.enterprise.publishing.remote.handler.ContentHandler.saveContent(ContentHandler.java:855) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.enterprise.publishing.remote.handler.ContentHandler.handleContents(ContentHandler.java:358) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.enterprise.publishing.remote.handler.ContentHandler.handle(ContentHandler.java:194) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.enterprise.publishing.remote.handler.ContentHandler.handle(ContentHandler.java:172) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.publisher.receiver.BundlePublisher.process(BundlePublisher.java:222) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.rest.PushPublisherJob.processBundle(PushPublisherJob.java:40) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.rest.PushPublisherJob.run(PushPublisherJob.java:67) ~[?:?]
dotcms-receiver-1      | 	at com.dotmarketing.quartz.DotJob.execute(DotJob.java:42) ~[?:?]
dotcms-receiver-1      | 	at org.quartz.core.JobRunShell.run(JobRunShell.java:223) ~[?:?]
dotcms-receiver-1      | 	at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:549) ~[?:?]
dotcms-receiver-1      | 17:46:12.109  WARN  business.ESContentletAPIImpl - Contentlet is currently marked as 'Archived'.
dotcms-receiver-1      | com.dotmarketing.exception.DotDataException: Contentlet is currently marked as 'Archived'.
dotcms-receiver-1      | 	at com.dotcms.content.elasticsearch.business.ESContentletAPIImpl.updateTemplateInAllLanguageVersions(ESContentletAPIImpl.java:6809) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.content.elasticsearch.business.ESContentletAPIImpl.internalCheckin(ESContentletAPIImpl.java:5767) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.content.elasticsearch.business.ESContentletAPIImpl.lambda$checkin$63(ESContentletAPIImpl.java:5301) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.concurrent.lock.StripedLockImpl.tryLock(StripedLockImpl.java:100) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.concurrent.lock.StripedLockImpl.tryLock(StripedLockImpl.java:55) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.concurrent.lock.IdentifierStripedLock.tryLock(IdentifierStripedLock.java:16) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.content.elasticsearch.business.ESContentletAPIImpl.checkin(ESContentletAPIImpl.java:5300) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.content.elasticsearch.business.ESContentletAPIImpl.checkin(ESContentletAPIImpl.java:10395) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.content.elasticsearch.business.ESContentletAPIImpl.checkin(ESContentletAPIImpl.java:5142) ~[?:?]
dotcms-receiver-1      | 	at com.dotmarketing.portlets.contentlet.business.ContentletAPIInterceptor.checkin(ContentletAPIInterceptor.java:286) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.enterprise.publishing.remote.handler.ContentHandler.saveContent(ContentHandler.java:855) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.enterprise.publishing.remote.handler.ContentHandler.handleContents(ContentHandler.java:358) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.enterprise.publishing.remote.handler.ContentHandler.handle(ContentHandler.java:194) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.enterprise.publishing.remote.handler.ContentHandler.handle(ContentHandler.java:172) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.publisher.receiver.BundlePublisher.process(BundlePublisher.java:222) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.rest.PushPublisherJob.processBundle(PushPublisherJob.java:40) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.rest.PushPublisherJob.run(PushPublisherJob.java:67) ~[?:?]
dotcms-receiver-1      | 	at com.dotmarketing.quartz.DotJob.execute(DotJob.java:42) ~[?:?]
dotcms-receiver-1      | 	at org.quartz.core.JobRunShell.run(JobRunShell.java:223) ~[?:?]
dotcms-receiver-1      | 	at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:549) ~[?:?]
dotcms-receiver-1      | 17:46:12.109  ERROR handler.ContentHandler - An error occurred when processing Contentlet in '/data/shared/assets/bundles/01KTM5F29YXWE2V1FREEFW2TXK/working/demo.dotcms.com/2/0-test-page.content.xml' with ID '902f839a441232cd7e278788c098b42f': 'Contentlet is currently marked as 'Archived'.'
dotcms-receiver-1      | com.dotmarketing.exception.DotDataException: Contentlet is currently marked as 'Archived'.
dotcms-receiver-1      | 	at com.dotcms.content.elasticsearch.business.ESContentletAPIImpl.updateTemplateInAllLanguageVersions(ESContentletAPIImpl.java:6809) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.content.elasticsearch.business.ESContentletAPIImpl.internalCheckin(ESContentletAPIImpl.java:5767) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.content.elasticsearch.business.ESContentletAPIImpl.lambda$checkin$63(ESContentletAPIImpl.java:5301) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.concurrent.lock.StripedLockImpl.tryLock(StripedLockImpl.java:100) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.concurrent.lock.StripedLockImpl.tryLock(StripedLockImpl.java:55) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.concurrent.lock.IdentifierStripedLock.tryLock(IdentifierStripedLock.java:16) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.content.elasticsearch.business.ESContentletAPIImpl.checkin(ESContentletAPIImpl.java:5300) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.content.elasticsearch.business.ESContentletAPIImpl.checkin(ESContentletAPIImpl.java:10395) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.content.elasticsearch.business.ESContentletAPIImpl.checkin(ESContentletAPIImpl.java:5142) ~[?:?]
dotcms-receiver-1      | 	at com.dotmarketing.portlets.contentlet.business.ContentletAPIInterceptor.checkin(ContentletAPIInterceptor.java:286) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.enterprise.publishing.remote.handler.ContentHandler.saveContent(ContentHandler.java:855) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.enterprise.publishing.remote.handler.ContentHandler.handleContents(ContentHandler.java:358) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.enterprise.publishing.remote.handler.ContentHandler.handle(ContentHandler.java:194) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.enterprise.publishing.remote.handler.ContentHandler.handle(ContentHandler.java:172) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.publisher.receiver.BundlePublisher.process(BundlePublisher.java:222) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.rest.PushPublisherJob.processBundle(PushPublisherJob.java:40) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.rest.PushPublisherJob.run(PushPublisherJob.java:67) ~[?:?]
dotcms-receiver-1      | 	at com.dotmarketing.quartz.DotJob.execute(DotJob.java:42) ~[?:?]
dotcms-receiver-1      | 	at org.quartz.core.JobRunShell.run(JobRunShell.java:223) ~[?:?]
dotcms-receiver-1      | 	at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:549) ~[?:?]
dotcms-receiver-1      | 17:46:12.111  ERROR receiver.BundlePublisher - Error publishing bundle with ID '01KTM5F29YXWE2V1FREEFW2TXK.tar.gz': An error occurred when processing Contentlet in '/data/shared/assets/bundles/01KTM5F29YXWE2V1FREEFW2TXK/working/demo.dotcms.com/2/0-test-page.content.xml' with ID '902f839a441232cd7e278788c098b42f': 'Contentlet is currently marked as 'Archived'.'
dotcms-receiver-1      | com.dotcms.publishing.DotPublishingException: An error occurred when processing Contentlet in '/data/shared/assets/bundles/01KTM5F29YXWE2V1FREEFW2TXK/working/demo.dotcms.com/2/0-test-page.content.xml' with ID '902f839a441232cd7e278788c098b42f': 'Contentlet is currently marked as 'Archived'.'
dotcms-receiver-1      | 	at com.dotcms.enterprise.publishing.remote.handler.ContentHandler.handleContents(ContentHandler.java:606) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.enterprise.publishing.remote.handler.ContentHandler.handle(ContentHandler.java:194) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.enterprise.publishing.remote.handler.ContentHandler.handle(ContentHandler.java:172) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.publisher.receiver.BundlePublisher.process(BundlePublisher.java:222) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.rest.PushPublisherJob.processBundle(PushPublisherJob.java:40) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.rest.PushPublisherJob.run(PushPublisherJob.java:67) ~[?:?]
dotcms-receiver-1      | 	at com.dotmarketing.quartz.DotJob.execute(DotJob.java:42) ~[?:?]
dotcms-receiver-1      | 	at org.quartz.core.JobRunShell.run(JobRunShell.java:223) ~[?:?]
dotcms-receiver-1      | 	at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:549) ~[?:?]
dotcms-receiver-1      | Caused by: com.dotmarketing.exception.DotDataException: Contentlet is currently marked as 'Archived'.
dotcms-receiver-1      | 	at com.dotcms.content.elasticsearch.business.ESContentletAPIImpl.updateTemplateInAllLanguageVersions(ESContentletAPIImpl.java:6809) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.content.elasticsearch.business.ESContentletAPIImpl.internalCheckin(ESContentletAPIImpl.java:5767) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.content.elasticsearch.business.ESContentletAPIImpl.lambda$checkin$63(ESContentletAPIImpl.java:5301) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.concurrent.lock.StripedLockImpl.tryLock(StripedLockImpl.java:100) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.concurrent.lock.StripedLockImpl.tryLock(StripedLockImpl.java:55) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.concurrent.lock.IdentifierStripedLock.tryLock(IdentifierStripedLock.java:16) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.content.elasticsearch.business.ESContentletAPIImpl.checkin(ESContentletAPIImpl.java:5300) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.content.elasticsearch.business.ESContentletAPIImpl.checkin(ESContentletAPIImpl.java:10395) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.content.elasticsearch.business.ESContentletAPIImpl.checkin(ESContentletAPIImpl.java:5142) ~[?:?]
dotcms-receiver-1      | 	at com.dotmarketing.portlets.contentlet.business.ContentletAPIInterceptor.checkin(ContentletAPIInterceptor.java:286) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.enterprise.publishing.remote.handler.ContentHandler.saveContent(ContentHandler.java:855) ~[?:?]
dotcms-receiver-1      | 	at com.dotcms.enterprise.publishing.remote.handler.ContentHandler.handleContents(ContentHandler.java:358) ~[?:?]
dotcms-receiver-1      | 	... 8 more
dotcms-receiver-1      | 17:46:12.115  ERROR Failed to publish because an error occurred:  - Error publishing bundle with ID '01KTM5F29YXWE2V1FREEFW2TXK.tar.gz': com.dotcms.publishing.DotPublishingException: An error occurred when processing Contentlet in '/data/shared/assets/bundles/01KTM5F29YXWE2V1FREEFW2TXK/working/demo.dotcms.com/2/0-test-page.content.xml' with ID '902f839a441232cd7e278788c098b42f': 'Contentlet is currently marked as 'Archived'.'
dotcms-receiver-1      | 17:46:12.115  INFO  util.PushPublishLogger - class com.dotcms.rest.PushPublisherJob : [BundleID: 01KTM5F29YXWE2V1FREEFW2TXK.tar.gz] Finished bundle publish process
dotcms-receiver-1      | 17:46:12.115  INFO  rest.PushPublisherJob - Finished bundle publish process

Steps to Reproduce

  1. Set up a dotCMS push publishing environment (sender + receiver).
  2. Make sure both the sender and receiver instances already have the same Site configured.
  3. In the sender, create an HTML Page and add content for both English and Spanish (i.e., two language versions of the same page).
  4. Un-publish and archive both language versions of the page on the sender.
  5. Push-publish either of the language versions to the receiver.

Expected: The push publishing process succeeds and the page is correctly archived on the receiver.

Actual: The push fails with DotDataException: Contentlet is currently marked as 'Archived'. and the entire bundle is marked as failed.


Acceptance Criteria

  • Pushing an archived HTML Page that has multiple language versions succeeds on the receiver without errors.
  • After the push, the page is correctly archived on the receiver for all language versions that were included in the bundle.
  • Non-archived multi-language HTML pages continue to push publish correctly (no regression).
  • Push-publishing a whole Site that contains archived multi-language pages does not fail.

dotCMS Version

Latest from main branch. Also confirmed on production instances (see customer log from 01KTCYF028BWV1XE3JKXWQJ4AQ).


Severity

High — Major functionality broken. Push-publishing an entire Site reliably fails whenever that Site contains archived HTML pages with multiple language versions. There is no workaround other than permanently deleting (not archiving) those pages before pushing.


Proposed Fix

Location

ESContentletAPIImpl.javaupdateTemplateInAllLanguageVersions (private method, ~line 6781).

What to change

1. Add an early return at the top of the method when the incoming contentlet is itself archived:

if (contentlet.getBoolProperty(DO_NOT_UPDATE_TEMPLATES)) {
    return;
}
// Template propagation is irrelevant for archived pages. This guard prevents a
// DotDataException when pushing multiple language versions of an archived HTML page,
// where the first processed language is archived on the receiver before the second
// is checked in.
if (contentlet.isArchived()) {
    return;
}

2. Change the else if branch that currently throws DotDataException to log a warning and return:

// Before (line ~6808):
} else if (contentletByIdentifierAnyLanguageArchived.isArchived()) {
    throw new DotDataException(String.format(
            "Contentlet with ID '%s' is currently marked as 'Archived'.",
            contentlet.getIdentifier()));
}

// After:
} else if (contentletByIdentifierAnyLanguageArchived.isArchived()) {
    Logger.warn(ESContentletAPIImpl.class, String.format(
            "Skipping template propagation: all existing versions of Contentlet '%s' are archived.",
            contentlet.getIdentifier()));
    return;
}

Why this won't break existing functionality

updateTemplateInAllLanguageVersions is a private method. No code outside ESContentletAPIImpl can call it directly, so there is no external caller that could be relying on the DotDataException being thrown.

The only path into this method is through checkininternalCheckin. The call site in internalCheckin (lines 5765–5770) already demonstrates the intended behavior: it wraps the call in a try/catch that silently swallows NotFoundInDbException (the "content is brand new" case). The DotDataException from the archived case was never caught there — it was an unintended gap that caused push publishing to explode.

Any code that calls checkin and catches DotDataException does so to handle generic data-layer failures. Removing this specific throw does not deprive those callers of any meaningful signal — if they need to know whether a contentlet is archived, the correct way is contentlet.isArchived(), not catching a side-effect exception from a template-propagation helper.

The UI is already protected separately. The dotCMS UI prevents editing archived pages before you reach checkin. The throw at line 6809 was a defensive guard for a state that the UI makes unreachable in normal interactive use. Push publishing is the only realistic path that legitimately triggers it, and the throw is wrong there.

The semantic change is correct. The purpose of updateTemplateInAllLanguageVersions is to propagate a template change across active language versions of the same HTML page. Archived pages are not served and do not need template synchronization. Returning early for archived content is the right behavior, not throwing.


Links

Metadata

Metadata

Assignees

Type

No type

Fields

No fields configured for issues without a type.

Projects

Status
Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions