{"record":{"id":"885042465f2c62a8","repo":"JetBrains/intellij-community","slug":"illegal-position-specifier-0-in-1","errorCode":null,"errorMessage":"illegal position specifier ''{0}'' in ''{1}''","messagePattern":"illegal position specifier ''(.+?)'' in ''(.+?)''","errorType":"exception","errorClass":"FormatDecode.IllegalFormatException","httpStatus":null,"severity":"error","filePath":"java/java-analysis-impl/src/com/siyeh/ig/format/FormatDecode.java","lineNumber":285,"sourceCode":"        }\n        checkNoPrecision(precision, specifier);\n        continue;\n      }\n      else if (\"%\".equals(conversion)) { // literal '%'\n        checkFlags(flagBits, LEFT_JUSTIFY, specifier);\n        checkNoPrecision(precision, specifier);\n        continue;\n      }\n\n      if (posSpec != null) {\n        if (isAllBitsSet(flagBits, PREVIOUS)) {\n          throw new IllegalFormatException(\n            InspectionGadgetsBundle.message(\"format.string.error.unnecessary.position.specifier\", posSpec, specifier));\n        }\n        final String num = posSpec.substring(0, posSpec.length() - 1);\n        pos = Integer.parseInt(num) - 1;\n        if (pos < 0) {\n          throw new IllegalFormatException(\n            InspectionGadgetsBundle.message(\"format.string.error.illegal.position.specifier\", posSpec, specifier));\n        }\n        previousAllowed = true;\n      }\n      else if (isAllBitsSet(flagBits, PREVIOUS)) {\n        // reuse last pos\n        if (!previousAllowed) {\n          throw new IllegalFormatException(InspectionGadgetsBundle.message(\"format.string.error.previous.element.not.found\", specifier));\n        }\n      }\n      else {\n        previousAllowed = true;\n        pos = implicit++;\n      }\n\n      final Validator allowed;\n      if (isAllVerifier) {\n        allowed = new AllValidatorWithRange(TextRange.create(matcher.start(), matcher.end()),","sourceCodeStart":267,"sourceCodeEnd":303,"githubUrl":"https://github.com/JetBrains/intellij-community/blob/be881553f2a76ac8b4ea53950d93f0de78295c19/java/java-analysis-impl/src/com/siyeh/ig/format/FormatDecode.java#L267-L303","documentation":"IllegalFormatException from FormatDecode when an argument position specifier is '0$', i.e. pos = Integer.parseInt(num) - 1 < 0. Argument indices are 1-based; '%0$s' has no valid target and throws, matching java.util.Formatter's illegal index. Higher indices that exceed the argument count are validated later by the argument-count checks.","triggerScenarios":"A specifier '%0$s' / '%0$d' in the format string; posSpec parsed, minus one yields -1.","commonSituations":"Off-by-one bugs when generating indices programmatically (loop starting at 0 emitting 'i$'); templates substituting a zero counter; misunderstanding 1-based indexing.","solutions":["Change the index to 1-based: '%1$s' not '%0$s'","When generating specifiers in a loop, emit (i + 1) + '$'"],"exampleFix":"// before\nString.format(\"%0$s!\", \"hi\")\n// after\nString.format(\"%1$s!\", \"hi\")","handlingStrategy":"validation","validationCode":"static boolean positionSpecValid(String posSpec) {\n  if (posSpec == null) return true;\n  try { return Integer.parseInt(posSpec.substring(0, posSpec.length() - 1)) >= 1; }\n  catch (NumberFormatException e) { return false; }\n}","typeGuard":null,"tryCatchPattern":"try { FormatDecode.decode(fmt, argc); } catch (IllegalFormatException e) { /* fix 0$ to 1$-based index */ }","preventionTips":["Remember argument indices are 1-based in java.util.Formatter","Emit (i + 1) + '$' when generating indices in loops"],"tags":["intellij","format-string","inspection","java","off-by-one"],"backgroundTag":null,"analyzedSha":"be881553f2a76ac8b4ea53950d93f0de78295c19","analyzedAt":"2026-08-14T14:13:06.425Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}