{"record":{"id":"1e9c566ae04e1f57","repo":"jax-ml/jax","slug":"the-format-string-expects-n-placeholders-argumen","errorCode":null,"errorMessage":"The format string expects {n_placeholders} argument{'' if n_placeholders == 1 else 's'}, but got {len(args)}","messagePattern":"The format string expects (.+?) argument(.+?), but got (.+?)","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"jax/_src/pallas/primitives.py","lineNumber":634,"sourceCode":"\ndef check_debug_print_format(\n    fmt: str, *args: jax_typing.ArrayLike\n):\n  n_placeholders = 0\n  for _, field, spec, conversion in string.Formatter().parse(fmt):\n    if field is not None:\n      n_placeholders += 1\n    if spec or conversion:\n      raise ValueError(\n          \"The format string should not contain any format specs or conversions\"\n      )\n    if field:\n      raise ValueError(\n          \"The format string should not reference arguments by position or name\"\n      )\n\n  if len(args) != n_placeholders:\n    raise TypeError(\n        f\"The format string expects {n_placeholders} \"\n        f\"argument{'' if n_placeholders == 1 else 's'}, but got {len(args)}\"\n    )\n\n\n# All of those shenanigans are because we can't make TransformedRef a PyTree,\n# because they should appear as atomic JAX values to the users.\n# TODO(apaszke): This can be deleted once we make transforms in Mosaic GPU\n# inferred by the compiler.\ndef wrap_with_transforms(\n    fun: Callable,\n    ref_transforms: tuple[tuple[state_types.Transform, ...], ...],\n) -> Callable:\n  if all(not t for t in ref_transforms):\n    return fun\n  def wrapped(*args, **kwargs):\n    args_ft = ft.flatten(\n        (args, kwargs), registry=tree_util.default_registry","sourceCodeStart":616,"sourceCodeEnd":652,"githubUrl":"https://github.com/jax-ml/jax/blob/1e1c6a8fc06dfcd1247076ec5cae4640cea5d7bb/jax/_src/pallas/primitives.py#L616-L652","documentation":"Thrown by check_debug_print_format when a Pallas debug_print (or DeviceArray.print in a kernel) format string contains a different number of placeholders than the number of array arguments passed. The formatter only supports positional {} placeholders, one per argument.","triggerScenarios":"Calling debug_print(\"{} {}\", x) inside a Pallas kernel, or passing extra/missing arguments relative to the {} count in the format string.","commonSituations":"Editing a kernel's print statement and forgetting to add/remove a {} or an argument; copying Python f-string style formatting into debug_print which only supports str.format-style placeholders.","solutions":["Make the number of {} placeholders in the format string exactly equal the number of positional arguments","Do not use positional {0} or named {x} placeholders — they are rejected separately","Rewrite f-string style messages as \"value: {}\" with the value passed as an argument"],"exampleFix":"// before\ndebug_print(\"x={x} y\", x, y)\n// after\ndebug_print(\"x={} y={}\", x, y)","handlingStrategy":"validation","validationCode":"fmt = \"x={} y={}\"\nargs = (x, y)\nassert fmt.count(\"{}\") == len(args), f\"format expects {fmt.count('{}')} args, got {len(args)}\"","typeGuard":null,"tryCatchPattern":"try:\n    debug_print(fmt, *args)\nexcept (TypeError, ValueError) as e:\n    if \"format string\" in str(e):\n        raise ValueError(f\"debug_print format/arg mismatch: {fmt!r} vs {len(args)} args\") from e\n    raise","preventionTips":["Count {} placeholders against arguments before each debug_print","Never use {0} or named placeholders in Pallas debug_print","Keep a lint/CI check on format strings in kernel code"],"tags":["pallas","format-string","debug-print","jax"],"backgroundTag":"format-string-argument-mismatch","analyzedSha":"1e1c6a8fc06dfcd1247076ec5cae4640cea5d7bb","analyzedAt":"2026-08-27T09:53:25.647Z","schemaVersion":2},"datasetVersion":"2026-08-27T13:17:12.746Z"}