languagetool-org/languagetool · error · BadRequestException

'callback' value must match [a-zA-Z]+: '{callback}'

Error message

'callback' value must match [a-zA-Z]+: '{callback}'

What it means

The JSONP 'callback' parameter must consist only of ASCII letters ([a-zA-Z]+); anything else (digits, underscores, dots, brackets) is rejected to prevent injection into the JSONP response body. The constructor of the request-validation class enforces this regex whenever callback is non-null.

Source

Thrown at languagetool-server/src/main/java/org/languagetool/server/TextChecker.java:1090

    QueryParams(List<Language> altLanguages, List<String> enabledRules, List<String> disabledRules, List<CategoryId> enabledCategories, List<CategoryId> disabledCategories,
                boolean useEnabledOnly, boolean useQuerySettings, boolean allowIncompleteResults, boolean enableHiddenRules, boolean premium, boolean enableTempOffRules, JLanguageTool.Mode mode, JLanguageTool.Level level, Set<ToneTag> toneTags, @Nullable String callback, boolean inputLogging) {
      this.altLanguages = Objects.requireNonNull(altLanguages);
      this.enabledRules = enabledRules;
      this.disabledRules = disabledRules;
      this.enabledCategories = enabledCategories;
      this.disabledCategories = disabledCategories;
      this.useEnabledOnly = useEnabledOnly;
      this.useQuerySettings = useQuerySettings;
      this.allowIncompleteResults = allowIncompleteResults;
      this.enableHiddenRules = enableHiddenRules;
      this.premium = premium;
      this.enableTempOffRules = enableTempOffRules;
      this.regressionTestMode = enableTempOffRules;
      this.mode = Objects.requireNonNull(mode);
      this.level = Objects.requireNonNull(level);
      this.toneTags = toneTags;
      if (callback != null && !callback.matches("[a-zA-Z]+")) {
        throw new BadRequestException("'callback' value must match [a-zA-Z]+: '" + callback + "'");
      }
      this.callback = callback;
      this.inputLogging = inputLogging;
    }

    @Override
    public int hashCode() {
      return new HashCodeBuilder()
        .append(altLanguages)
        .append(enabledRules)
        .append(disabledRules)
        .append(enabledCategories)
        .append(disabledCategories)
        .append(useEnabledOnly)
        .append(useQuerySettings)
        .append(allowIncompleteResults)
        .append(enableHiddenRules)
        .append(premium)

View on GitHub (pinned to 2e990059ce)

Solutions

  1. Use a callback of letters only, e.g. 'myCallback' or 'jsonpCallback'
  2. Sanitize/strip non-letter characters from the callback before sending
  3. Generate a letters-only random suffix if uniqueness is needed (e.g. 'callbackXyzAbc')
  4. Drop the callback parameter entirely and consume plain JSON with fetch/axios

Example fix

// before
params.put("callback", "jQuery123_456");
// after
params.put("callback", "jQueryCallback");
Defensive patterns

Strategy: validation

Validate before calling

if (!/^[a-zA-Z]+$/.test(callback)) throw new Error('callback must be letters only');

Type guard

const isValidCallback = (cb) => typeof cb === 'string' && /^[a-zA-Z]+$/.test(cb);

Prevention

When it happens

Trigger: Calling /v2/check with callback=my_func, callback=cb1, callback=jQuery123... — any callback containing non-letter characters.

Common situations: Frameworks auto-generating callbacks like jQuery12345_ or __jsonp_cb_0; developers adding underscores for readability; client-side JSONP helpers that append counters.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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