JetBrains/intellij-community · error · FormatDecode.IllegalFormatException
illegal position specifier ''{0}'' in ''{1}''
Error message
illegal position specifier ''{0}'' in ''{1}'' What it means
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.
Source
Thrown at java/java-analysis-impl/src/com/siyeh/ig/format/FormatDecode.java:285
}
checkNoPrecision(precision, specifier);
continue;
}
else if ("%".equals(conversion)) { // literal '%'
checkFlags(flagBits, LEFT_JUSTIFY, specifier);
checkNoPrecision(precision, specifier);
continue;
}
if (posSpec != null) {
if (isAllBitsSet(flagBits, PREVIOUS)) {
throw new IllegalFormatException(
InspectionGadgetsBundle.message("format.string.error.unnecessary.position.specifier", posSpec, specifier));
}
final String num = posSpec.substring(0, posSpec.length() - 1);
pos = Integer.parseInt(num) - 1;
if (pos < 0) {
throw new IllegalFormatException(
InspectionGadgetsBundle.message("format.string.error.illegal.position.specifier", posSpec, specifier));
}
previousAllowed = true;
}
else if (isAllBitsSet(flagBits, PREVIOUS)) {
// reuse last pos
if (!previousAllowed) {
throw new IllegalFormatException(InspectionGadgetsBundle.message("format.string.error.previous.element.not.found", specifier));
}
}
else {
previousAllowed = true;
pos = implicit++;
}
final Validator allowed;
if (isAllVerifier) {
allowed = new AllValidatorWithRange(TextRange.create(matcher.start(), matcher.end()),View on GitHub (pinned to be881553f2)
Solutions
- Change the index to 1-based: '%1$s' not '%0$s'
- When generating specifiers in a loop, emit (i + 1) + '$'
Example fix
// before
String.format("%0$s!", "hi")
// after
String.format("%1$s!", "hi") Defensive patterns
Strategy: validation
Validate before calling
static boolean positionSpecValid(String posSpec) {
if (posSpec == null) return true;
try { return Integer.parseInt(posSpec.substring(0, posSpec.length() - 1)) >= 1; }
catch (NumberFormatException e) { return false; }
} Try / catch
try { FormatDecode.decode(fmt, argc); } catch (IllegalFormatException e) { /* fix 0$ to 1$-based index */ } Prevention
- Remember argument indices are 1-based in java.util.Formatter
- Emit (i + 1) + '$' when generating indices in loops
When it happens
Trigger: A specifier '%0$s' / '%0$d' in the format string; posSpec parsed, minus one yields -1.
Common situations: Off-by-one bugs when generating indices programmatically (loop starting at 0 emitting 'i$'); templates substituting a zero counter; misunderstanding 1-based indexing.
Related errors
- {2, choice, 1#flag|1<flags} ''{0}'' not allowed in ''{1}''
- unexpected character ''{0}'' in ''{1}''
- duplicate flag ''{0}'' in ''{1}''
- width (''{0}'') not allowed in ''{1}''
- unnecessary argument position specifier ''{0}'' in ''{1}''
AI-assisted analysis of JetBrains/intellij-community@be881553f2 (2026-08-14).
Data as JSON: /api/errors/885042465f2c62a8.
Report an issue: GitHub.