langchain-ai/langchain · error · ValueError

Invalid format specifier in f-string template. Nested replac

Error message

Invalid format specifier in f-string template. Nested replacement fields are not allowed.

What it means

Raised by `validate_f_string_template` when a replacement field's format specifier itself contains braces, e.g. `{value:>{width}}`. Nested replacement fields are legal in raw Python f-strings but LangChain's template formatting does not support them, so validation rejects the template with `ValueError` before any render attempt.

Source

Thrown at libs/core/langchain_core/prompts/string.py:255

                f"Variable names cannot contain attribute "
                f"access (.) or indexing ([])."
            )
            raise ValueError(msg)

        if var.isdigit():
            msg = (
                f"Invalid variable name {var!r} in f-string template. "
                f"Variable names cannot be all digits as they are interpreted "
                f"as positional arguments."
            )
            raise ValueError(msg)

        if format_spec and ("{" in format_spec or "}" in format_spec):
            msg = (
                "Invalid format specifier in f-string template. "
                "Nested replacement fields are not allowed."
            )
            raise ValueError(msg)

        input_variables.add(var)

    return sorted(input_variables)


def check_valid_template(
    template: str, template_format: str, input_variables: list[str]
) -> None:
    """Check that template string is valid.

    Args:
        template: The template string.
        template_format: The template format.

            Should be one of `'f-string'` or `'jinja2'`.
        input_variables: The input variables.

View on GitHub (pinned to e32fa9a52e)

Solutions

  1. Hard-code the width in the format spec: `{price:>10.2f}`
  2. Compute the padded string in Python before formatting the prompt: `padded = f"{price:>{width}.2f}"` then use `{padded}` in the template
  3. For loop-driven dynamic layout, render the whole fragment with `template_format="jinja2"` and Jinja2 filters (requires jinja2 installed)

Example fix

# before
prompt = PromptTemplate.from_template("total: {price:>{width}.2f}")

# after
padded_total = f"{price:>{width}.2f}"
prompt = PromptTemplate.from_template("total: {padded_total}")
prompt.format(padded_total=padded_total)
Defensive patterns

Strategy: validation

Validate before calling

import re

def has_no_nested_format_spec(template: str) -> bool:
    for spec in re.findall(r"\{[^{}]+:([^{}]*)\}", template):
        if "{" in spec or "}" in spec:
            return False
    return True

assert has_no_nested_format_spec(template)

Prevention

When it happens

Trigger: A template like `"{price:>{width}.2f}"` or `{num:{fill}<{width}}` used with the default f-string `template_format`. The check `format_spec and ("{" in format_spec or "}" in format_spec)` trips as soon as the field is parsed.

Common situations: Porting display/alignment code that uses dynamic widths into prompt templates; attempting table-style padding inside prompts; copy-pasting f-strings from Python formatting guides.

Related errors


AI-assisted analysis of langchain-ai/langchain@e32fa9a52e (2026-08-14). Data as JSON: /api/errors/6c10261590b2a923. Report an issue: GitHub.