{"record":{"id":"453be76fed13e90d","repo":"SonarSource/sonarqube","slug":"locale-cannot-be-parsed-as-a-bcp47-language-tag","errorCode":null,"errorMessage":"Locale cannot be parsed as a BCP47 language tag","messagePattern":"Locale cannot be parsed as a BCP47 language tag","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":400,"severity":"error","filePath":"server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/IndexAction.java","lineNumber":79,"sourceCode":"      .setDefaultValue(ENGLISH.toLanguageTag());\n    indexAction.createParam(TS_PARAM)\n      .setDescription(\"Date of the last cache update.\")\n      .setExampleValue(\"2014-06-04T09:31:42+0000\");\n  }\n\n  @Override\n  public void handle(Request request, Response response) throws Exception {\n    Date timestamp = request.paramAsDateTime(TS_PARAM);\n    if (timestamp != null && timestamp.after(server.getStartedAt())) {\n      response.stream().setStatus(HTTP_NOT_MODIFIED).output().close();\n      return;\n    }\n    String localeParam = request.mandatoryParam(LOCALE_PARAM);\n    Locale locale = Locale.forLanguageTag(localeParam);\n    try {\n      checkArgument(!locale.getISO3Language().isEmpty(), INVALID_LANGUAGE_TAG_MESSAGE);\n    } catch (MissingResourceException e) {\n      throw new IllegalArgumentException(INVALID_LANGUAGE_TAG_MESSAGE, e);\n    }\n\n    try (JsonWriter json = response.newJsonWriter()) {\n      json.beginObject();\n      json.prop(\"effectiveLocale\", i18n.getEffectiveLocale(locale).toLanguageTag());\n      json.name(\"messages\");\n      json.beginObject();\n      i18n.getPropertyKeys().forEach(messageKey -> json.prop(messageKey, i18n.message(locale, messageKey, messageKey)));\n      json.endObject();\n      json.endObject();\n    }\n  }\n}\n","sourceCodeStart":61,"sourceCodeEnd":93,"githubUrl":"https://github.com/SonarSource/sonarqube/blob/184c821202192afc1c599fc912d0889b69fffa53/server/sonar-webserver-webapi/src/main/java/org/sonar/server/platform/ws/IndexAction.java#L61-L93","documentation":"IndexAction (api/system/index) validates the mandatory locale parameter by parsing it as a BCP47 language tag: Locale.forLanguageTag plus getISO3Language must resolve to a known ISO3 language. If the tag is empty after parsing or unknown (MissingResourceException), IllegalArgumentException is thrown.","triggerScenarios":"GET api/system/index with locale values like 'xx', 'qq-US', empty-ish tags, or strings that Locale.forLanguageTag silently maps to an undetermined locale; malformed tags with invalid characters.","commonSituations":"Passing custom/internal locale codes not in BCP 47; passing language names ('english') instead of tags ('en'); passing locales unsupported by the JVM's CLDR data.","solutions":["Use a valid BCP 47 language tag such as en, fr, de-DE","Validate the tag client-side with Locale.forLanguageTag(tag).getISO3Language() before calling","Check supported locales against the installed language packs; remove exotic/custom locale codes"],"exampleFix":"// before\nconst locale = 'english'; // invalid\nawait fetch(`/api/system/index?locale=${locale}`);\n\n// after\nconst locale = 'en';\nawait fetch(`/api/system/index?locale=${encodeURIComponent(locale)}`);","handlingStrategy":"validation","validationCode":"function isBcp47Tag(tag) { try { return Intl.getCanonicalLocales(tag).length > 0; } catch { return false; } }\nif (!isBcp47Tag(locale)) throw new Error('invalid language tag');","typeGuard":"function isValidLocaleTag(tag) { return typeof tag === 'string' && /^[a-zA-Z]{2,3}(-[a-zA-Z0-9]+)*$/.test(tag); }","tryCatchPattern":"try { await get(`/api/system/index?locale=${locale}`); } catch (e) { if (e.message.includes('BCP47')) { return get('/api/system/index?locale=en'); } throw e; }","preventionTips":["Always use standard BCP 47 tags (en, fr, de-DE), never language names","Canonicalize user-provided locales with Intl APIs before sending","Restrict choices to locales installed on the SonarQube instance"],"tags":["webapi","i18n","locale","validation"],"backgroundTag":"invalid-argument-format","analyzedSha":"184c821202192afc1c599fc912d0889b69fffa53","analyzedAt":"2026-09-09T12:23:51.573Z","contentChangedAt":"2026-09-09T12:23:51.573Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}