LanguagesResource.getAllMessages throws IllegalStateException on duplicate language keys
Nobody has claimed this yet.
- 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
- Have (or create) a custom content portlet whose title language key
com.dotcms.repackage.javax.portlet.title.<portletId>exists more than once in thelanguage_keytable for a locale — e.g. a custom portlet "Pages" (c-Pages). This can occur after saving/re-saving a custom portlet. - Log into the back-end and open a portlet (e.g. Site Browser), which makes the admin UI request all i18n messages (
LanguagesResource.getAllMessages). - Observed: the request returns
500and the server logsIllegalStateException: Duplicate key com.dotcms.repackage.javax.portlet.title.c-Pages. - 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.getAllMessagesno 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 a500. - Investigate the source of the duplicate portlet-title
language_keyrows created viaPortletAPIImpl.savePortlet(com.dotcms.repackage.javax.portlet.title.<portletId>); either prevent duplicate insertion on re-save or document why duplicates are expected. - Regression coverage:
getAllMessagesreturns200with the full message set when duplicate keys are present.
dotCMS Version
Current Evergreen 26.07.13-1
Severity
Medium - Some functionality impacted
Links
- Related (context only, not a duplicate): #34435 (Portlet cache Redis fix — this was surfaced while testing it)
- Freshdesk ticket of the related issue: https://helpdesk.dotcms.com/a/tickets/34992
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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