{"record":{"id":"8286cf309863430f","repo":"bazelbuild/bazel","slug":"incomplete-format-pattern-ends-with-s","errorCode":null,"errorMessage":"incomplete format pattern ends with %: %s","messagePattern":"incomplete format pattern ends with %: (.+?)","errorType":"exception","errorClass":"MissingFormatWidthException","httpStatus":null,"severity":"error","filePath":"src/main/java/net/starlark/java/eval/Printer.java","lineNumber":400,"sourceCode":"    // whose constructor can take and display arbitrary error message, hence its use below.\n    // TODO(adonovan): this suggests we're using the wrong exception. Throw IAE?\n\n    int length = pattern.length();\n    int argLength = arguments.size();\n    int i = 0; // index of next character in pattern\n    int a = 0; // index of next argument in arguments\n\n    while (i < length) {\n      int p = pattern.indexOf('%', i);\n      if (p == -1) {\n        printer.append(pattern, i, length);\n        break;\n      }\n      if (p > i) {\n        printer.append(pattern, i, p);\n      }\n      if (p == length - 1) {\n        throw new MissingFormatWidthException(\n            \"incomplete format pattern ends with %: \" + Starlark.repr(pattern, semantics));\n      }\n      char conv = pattern.charAt(p + 1);\n      i = p + 2;\n\n      // %%: literal %\n      if (conv == '%') {\n        printer.append('%');\n        continue;\n      }\n\n      // get argument\n      if (a >= argLength) {\n        throw new MissingFormatWidthException(\n            \"not enough arguments for format pattern \"\n                + Starlark.repr(pattern, semantics)\n                + \": \"\n                + Starlark.repr(Tuple.copyOf(arguments), semantics));","sourceCodeStart":382,"sourceCodeEnd":418,"githubUrl":"https://github.com/bazelbuild/bazel/blob/e6e199d0601a244511b4cf18c8b2828aa73db1fd/src/main/java/net/starlark/java/eval/Printer.java#L382-L418","documentation":"Thrown by Starlark's printf-style %-formatting (Printer.formatString) when the pattern's last character is a lone '%'. A trailing % is treated as the start of a conversion but has no conversion character, so formatting aborts with MissingFormatWidthException carrying 'incomplete format pattern ends with %: <repr of pattern>'.","triggerScenarios":"\"100%\" % (), \"rate: %s%%\" works but \"rate: %s%\" % (x,) fails; any pattern built by concatenation that leaves a single % at the end, e.g. \"%s\" + \"%\".","commonSituations":"Building human-readable messages with percentages ('completed 50%'); concatenating a variable suffix onto a format string; templates authored by non-Python users who don't know % must be doubled.","solutions":["Escape the literal percent: use %% wherever a literal % is wanted, including at the end.","Prefer str.format-style or plain concatenation for text containing many literal percent signs.","Lint format patterns for a trailing single % before use."],"exampleFix":"# before\nmsg = \"progress: %d%\" % pct\n\n# after\nmsg = \"progress: %d%%\" % pct","handlingStrategy":"validation","validationCode":"def fmt(pattern):\n    if pattern.endswith(\"%\") and not pattern.endswith(\"%%\"):\n        fail(\"pattern ends with lone %: %r\" % pattern)\n    return pattern","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always write literal percent as %% in %-format strings.","Prefer str.format or concatenation for text dense with % signs."],"tags":["starlark","format","percent-format","escaping"],"backgroundTag":null,"analyzedSha":"e6e199d0601a244511b4cf18c8b2828aa73db1fd","analyzedAt":"2026-08-14T10:24:27.848Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}