dotCMS / dotCMS/core

LanguagesResource.getAllMessages throws IllegalStateException on duplicate language keys

Open
#36,689 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

dotCMS : Localization OKR : Customer Support Team : Maintenance Type : Defect
Dominant language
Java
Stars
970
Forks
486
Avg merge
3d 33m
Merged PRs (30d)
170

Description

Problem Statement

The back-end i18n messages endpoint (GET .../v2/languages/{lang}/messages, served by LanguagesResource.getAllMessages) throws a 500 / IllegalStateException: Duplicate key ... whenever the language keys for a locale contain two entries with the same key. This breaks the admin UI flow that loads all localized messages (e.g. opening the Site Browser portlet triggers the request).

Observed log:

WARN mapper.RuntimeExceptionMapper - Duplicate key com.dotcms.repackage.javax.portlet.title.c-Pages (attempted merging values Pages and Pages)
java.lang.IllegalStateException: Duplicate key com.dotcms.repackage.javax.portlet.title.c-Pages (attempted merging values Pages and Pages)
    at java.base/java.util.stream.Collectors.duplicateKeyException(Collectors.java:135)
    ...
    at com.dotcms.rest.api.v2.languages.LanguagesResource.getAllMessages(LanguagesResource.java:492)

Root cause: In LanguagesResource.getAllMessages, the language-keys branch (used when localization enhancements are disabled) collects with a strict Collectors.toMap that has no merge function:

// LanguagesResource.java ~L491-493
final Map<?,?> mapLanguageKeys = languageAPI
        .getLanguageKeys(currentLocale.getLanguage()).stream().collect(
                Collectors.toMap(LanguageKey::getKey, LanguageKey::getValue)); // ← throws on duplicate key

Collectors.toMap without a merge function throws on any duplicate key. Notably, the other two collectors in the very same method (the enhanced language-variables branch ~L482 and the legacy language-variables branch ~L501) already pass a merge function and only log a warning — this one branch is the outlier.

The duplicate data here is a portlet-title language key, com.dotcms.repackage.javax.portlet.title.c-Pages (both values Pages). These keys are written by PortletAPIImpl.savePortlet (com.dotcms.repackage.javax.portlet.title.<portletId>) across all languages, so re-saving a custom content portlet can leave duplicate rows in the language_key table, which then makes this endpoint fail.

Context: Found while QA-testing #34435 (Portlet cache / Redis fix). It is unrelated to that fix — different subsystem (i18n messages REST endpoint), different root cause (missing merge function + duplicate language_key rows). Filed separately per that investigation.

Steps to Reproduce

  1. Have (or create) a custom content portlet whose title language key com.dotcms.repackage.javax.portlet.title.<portletId> exists more than once in the language_key table for a locale — e.g. a custom portlet "Pages" (c-Pages). This can occur after saving/re-saving a custom portlet.
  2. Log into the back-end and open a portlet (e.g. Site Browser), which makes the admin UI request all i18n messages (LanguagesResource.getAllMessages).
  3. Observed: the request returns 500 and the server logs IllegalStateException: Duplicate key com.dotcms.repackage.javax.portlet.title.c-Pages.
  4. Expected: the endpoint returns the full message map, deterministically resolving the duplicate (as the sibling collectors in the same method already do).

Acceptance Criteria

  • LanguagesResource.getAllMessages no longer throws when the language keys for a locale contain duplicate keys; it resolves them deterministically with a merge function, consistent with the two sibling collectors in the same method.
  • A duplicate key is logged at warn (matching the existing branches) instead of aborting the whole response with a 500.
  • Investigate the source of the duplicate portlet-title language_key rows created via PortletAPIImpl.savePortlet (com.dotcms.repackage.javax.portlet.title.<portletId>); either prevent duplicate insertion on re-save or document why duplicates are expected.
  • Regression coverage: getAllMessages returns 200 with the full message set when duplicate keys are present.

dotCMS Version

Current Evergreen 26.07.13-1

Severity

Medium - Some functionality impacted

Links

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in LanguagesResource.getAllMessages around the language-keys collector near line 492, compare it with the two sibling collectors that already log duplicate keys, and trace duplicate portlet-title rows through PortletAPIImpl.savePortlet. Verify that duplicate keys produce a warning and a successful full message response, then investigate whether re-saving should prevent duplicate insertion or document why duplicates are expected.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
api, backend, internationalization
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
56/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.