languagetool-org/languagetool · error · RuntimeException

Language was already instantiated, see the cause stacktrace

Error message

Language was already instantiated, see the cause stacktrace below.

What it means

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).

Source

Thrown at languagetool-language-modules/es/src/main/java/org/languagetool/language/Spanish.java:55

import org.languagetool.tokenizers.SentenceTokenizer;
import org.languagetool.tokenizers.Tokenizer;
import org.languagetool.tokenizers.es.SpanishWordTokenizer;
import org.languagetool.tools.StringTools;

import java.io.IOException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Spanish extends LanguageWithModel {
  private static final String LANGUAGE_SHORT_CODE = "es-ES";

  private static volatile Throwable instantiationTrace;

  public Spanish() {
    Throwable trace = instantiationTrace;
    if (trace != null) {
      throw new RuntimeException("Language was already instantiated, see the cause stacktrace below.", trace);
    }
    instantiationTrace = new Throwable();
  }

  /**
   * This is a fake constructor overload for the subclasses. Public constructors can only be used by the LT itself.
   */
  protected Spanish(boolean fakeValue) {
  }

  @Override
  public String getName() {
    return "Spanish";
  }

  @Override
  public String getShortCode() {
    return "es";

View on GitHub (pinned to 2e990059ce)

Solutions

  1. Use Languages.getLanguageForShortCode("es") or Spanish.getInstance() instead of `new Spanish()`.
  2. Inspect the cause stack trace to find where the first instantiation happened and remove the duplicate.
  3. For subclassing, use the designated fake/package constructor overload meant for LT internals, not the public constructor.

Example fix

// before
Language es = new Spanish();
// after
Language es = Languages.getLanguageForShortCode("es");
Defensive patterns

Strategy: type-guard

Validate before calling

// Prefer registry access; check existing instance instead of constructing
Language lang = Languages.getLanguageForShortCode("es");
if (lang instanceof Spanish) { /* reuse lang */ }

Type guard

boolean isInstantiable(Language l) { return l == null; } // null means no instance yet; otherwise reuse

Try / catch

try {
  Language es = new Spanish();
} catch (RuntimeException e) {
  if (e.getMessage().contains("already instantiated")) {
    es = Languages.getLanguageForShortCode("es"); // reuse existing instance
  }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of languagetool-org/languagetool@2e990059ce (2026-09-06). Data as JSON: /api/errors/12744c2c99aafc56. Report an issue: GitHub.