{"record":{"id":"12744c2c99aafc56","repo":"languagetool-org/languagetool","slug":"language-was-already-instantiated-see-the-cause-s","errorCode":null,"errorMessage":"Language was already instantiated, see the cause stacktrace below.","messagePattern":"Language was already instantiated, see the cause stacktrace below\\.","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"languagetool-language-modules/es/src/main/java/org/languagetool/language/Spanish.java","lineNumber":55,"sourceCode":"import org.languagetool.tokenizers.SentenceTokenizer;\nimport org.languagetool.tokenizers.Tokenizer;\nimport org.languagetool.tokenizers.es.SpanishWordTokenizer;\nimport org.languagetool.tools.StringTools;\n\nimport java.io.IOException;\nimport java.util.*;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\npublic class Spanish extends LanguageWithModel {\n  private static final String LANGUAGE_SHORT_CODE = \"es-ES\";\n\n  private static volatile Throwable instantiationTrace;\n\n  public Spanish() {\n    Throwable trace = instantiationTrace;\n    if (trace != null) {\n      throw new RuntimeException(\"Language was already instantiated, see the cause stacktrace below.\", trace);\n    }\n    instantiationTrace = new Throwable();\n  }\n\n  /**\n   * This is a fake constructor overload for the subclasses. Public constructors can only be used by the LT itself.\n   */\n  protected Spanish(boolean fakeValue) {\n  }\n\n  @Override\n  public String getName() {\n    return \"Spanish\";\n  }\n\n  @Override\n  public String getShortCode() {\n    return \"es\";","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/languagetool-org/languagetool/blob/2e990059ce67d5e2a0f7f7ca5d31160c6709df4b/languagetool-language-modules/es/src/main/java/org/languagetool/language/Spanish.java#L37-L73","documentation":"Spanish's constructor enforces that the language class is instantiated only once per JVM. A static volatile instantiationTrace records the first construction; on a second construction the RuntimeException is thrown with the first stack trace as the cause, exposing hidden duplicate instantiation (e.g. via reflection or subclassing).","triggerScenarios":"Creating a second `new Spanish()` after one already exists; constructing Spanish through reflection, serialization, or subclass 'fake constructor overload' after the singleton was created by Languages.getLanguageForShortCode.","commonSituations":"Tests or tools directly instantiating Spanish instead of using Languages.getLanguageForShortCode(\"es\"); subclass constructors calling the public constructor; libraries embedding LanguageTool twice with different classloaders colliding on static state.","solutions":["Use Languages.getLanguageForShortCode(\"es\") or Spanish.getInstance() instead of `new Spanish()`.","Inspect the cause stack trace to find where the first instantiation happened and remove the duplicate.","For subclassing, use the designated fake/package constructor overload meant for LT internals, not the public constructor."],"exampleFix":"// before\nLanguage es = new Spanish();\n// after\nLanguage es = Languages.getLanguageForShortCode(\"es\");","handlingStrategy":"type-guard","validationCode":"// Prefer registry access; check existing instance instead of constructing\nLanguage lang = Languages.getLanguageForShortCode(\"es\");\nif (lang instanceof Spanish) { /* reuse lang */ }","typeGuard":"boolean isInstantiable(Language l) { return l == null; } // null means no instance yet; otherwise reuse","tryCatchPattern":"try {\n  Language es = new Spanish();\n} catch (RuntimeException e) {\n  if (e.getMessage().contains(\"already instantiated\")) {\n    es = Languages.getLanguageForShortCode(\"es\"); // reuse existing instance\n  }\n}","preventionTips":["Never call `new Spanish()`; always use Languages.getLanguageForShortCode(\"es\") or Spanish.getInstance()","Read the cause stack trace to locate the first instantiation site","In tests, reuse the Languages registry rather than constructing language objects"],"tags":["languagetool","spanish","singleton","instantiation"],"backgroundTag":"invalid-state-transition","analyzedSha":"2e990059ce67d5e2a0f7f7ca5d31160c6709df4b","analyzedAt":"2026-09-06T09:20:17.015Z","contentChangedAt":"2026-09-06T09:20:17.015Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}