languagetool-org/languagetool · error · RuntimeException
Could not map ${plainTextPosition} to original position. isT
Error message
Could not map ${plainTextPosition} to original position. isToPos: ${isToPos}, ${msg} What it means
After searching the markup-mapping for the entry closest to the requested plain-text position, getOriginalTextPositionFor() throws if no mapping entry matched at all (bestMatch == null). This means the internal mapping between plain text and original markup is empty or inconsistent with the requested position, so no original position can be produced.
Source
Thrown at languagetool-core/src/main/java/org/languagetool/markup/AnnotatedText.java:131
if (mapping.isEmpty()) {
return 0;
}
int minDiff = Integer.MAX_VALUE;
MappingValue bestMatch = null;
// algorithm: find the closest lower position
for (Map.Entry<Integer, MappingValue> entry : mapping.entrySet()) {
int maybeClosePosition = entry.getKey();
if (plainTextPosition < maybeClosePosition) {
int diff = maybeClosePosition - plainTextPosition;
if (diff > 0 && diff < minDiff) {
bestMatch = entry.getValue();
minDiff = diff;
}
}
}
if (bestMatch == null) {
String msg = "mappings: " + (mapping.size() < 5 ? mapping : mapping.size());
throw new RuntimeException("Could not map " + plainTextPosition + " to original position. isToPos: " + isToPos + ", " + msg);
}
// we remove markup total length if usage of fake markup and need from position
if (!isToPos && bestMatch.getFakeMarkupLength() > 0) {
minDiff = bestMatch.getFakeMarkupLength();
}
// We assume that when we have found the closest match there's a one-to-one mapping
// in this region, thus we can subtract 'minDiff' to get the exact position.
// If the bestMatch is a fakeMarkup, subtract it:
return bestMatch.getTotalPosition() - minDiff;
}
/**
* @since 3.9
*/
public String getGlobalMetaData(String key, String defaultValue) {
return customMetaData.getOrDefault(key, defaultValue);
}
View on GitHub (pinned to 2e990059ce)
Solutions
- Ensure rule-match offsets come from the same AnnotatedText/AnnotatedTextBuilder instance that is being checked.
- Verify the text was built via AnnotatedTextBuilder (addMarkup/addText) so the mapping is populated consistently.
- Guard against mapping/empty or out-of-range positions before calling the API.
- If it comes from a plugin/integration, upgrade LanguageTool — mapping logic changed across versions.
Example fix
// before
int orig = annotatedText.getOriginalTextPositionFor(ruleMatch.getFromPos(), false);
// after: build and use the same text instance for matching and mapping
AnnotatedText at = new AnnotatedTextBuilder().addText("...").build();
List<RuleMatch> ms = langTool.check(at);
int orig = at.getOriginalTextPositionFor(ms.get(0).getFromPos(), false); Defensive patterns
Strategy: try-catch
Try / catch
try {
int orig = text.getOriginalTextPositionFor(pos, false);
} catch (RuntimeException e) {
if (e.getMessage().startsWith("Could not map")) {
log.warn("Offset mapping failed; check text/mapping consistency", e);
}
} Prevention
- Use the same AnnotatedText instance for checking and offset mapping
- Build text via AnnotatedTextBuilder so the mapping is populated
- Upgrade LanguageTool if mapping bugs are suspected
When it happens
Trigger: Calling getOriginalTextPositionFor() on an AnnotatedText whose mapping produced no best match for plainTextPosition — e.g. the mapping is empty but not caught earlier, or position lies outside the mapped region and closest-match logic fails; thrown with isToPos and a debug dump of mappings (contents if <5 entries, else size).
Common situations: Rule match offsets computed against a different text object than the one being mapped (mismatched plain text vs markup); bugs in AnnotatedTextBuilder usage where text was added without matching markup entries.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
AI-assisted analysis of languagetool-org/languagetool@2e990059ce (2026-09-06).
Data as JSON: /api/errors/f0fdda147b0cbabf.
Report an issue: GitHub.