langchain-ai/langchain · error · ValueError

Invalid template: {tmpl}

Error message

Invalid template: {tmpl}

What it means

Inside the sequence-template loop of from_messages/from_template, an element that is neither str, dict, nor an accepted shape falls through to ValueError('Invalid template: {tmpl}'). Static analysis marks it unreachable because the loop's isinstance chain covers str and dict; reaching it requires an element type outside those branches (int, None, arbitrary objects) slipping into the template list.

Source

Thrown at libs/core/langchain_core/prompts/chat.py:531

                        msg = f"Invalid image template: {tmpl}"  # type: ignore[unreachable]
                        raise ValueError(msg)
                    prompt.append(img_template_obj)
                elif isinstance(tmpl, dict):
                    if template_format == "jinja2":
                        msg = (
                            "jinja2 is unsafe and is not supported for templates "
                            "expressed as dicts. Please use 'f-string' or 'mustache' "
                            "format."
                        )
                        raise ValueError(msg)
                    data_template_obj = DictPromptTemplate(
                        template=cast("dict[str, Any]", tmpl),
                        template_format=template_format,
                    )
                    prompt.append(data_template_obj)
                else:
                    msg = f"Invalid template: {tmpl}"  # type: ignore[unreachable]
                    raise ValueError(msg)
            return cls(prompt=prompt, **kwargs)
        msg = f"Invalid template: {template}"  # type: ignore[unreachable]
        raise ValueError(msg)

    @classmethod
    def from_template_file(
        cls: type[Self],
        template_file: str | Path,
        input_variables: list[str],
        **kwargs: Any,
    ) -> Self:
        """Create a class from a template file.

        Args:
            template_file: path to a template file.
            input_variables: list of input variables.
            **kwargs: Keyword arguments to pass to the constructor.

View on GitHub (pinned to e32fa9a52e)

Solutions

  1. Filter the list before passing: [t for t in templates if t is not None]
  2. Ensure every element is a str or dict as documented for list templates

Example fix

# before
parts = [system_prompt, user_section or None]
ChatPromptTemplate.from_messages(parts)  # ValueError on None

# after
parts = [p for p in [system_prompt, user_section] if p is not None]
ChatPromptTemplate.from_messages(parts)
Defensive patterns

Strategy: validation

Validate before calling

def clean_template_list(templates):
    bad = [t for t in templates if not isinstance(t, (str, dict))]
    if bad:
        msg = f"non-str/dict template elements: {bad!r}"
        raise ValueError(msg)
    return templates

Type guard

def is_valid_template_element(t) -> bool:
    return isinstance(t, (str, dict))

Prevention

When it happens

Trigger: Passing a list-of-templates element like 42, None, or a custom object: ChatPromptTemplate.from_messages(['system: hi', None]) — the None is neither str nor dict and trips the fallthrough.

Common situations: Optional message sections concatenated with 'or []' patterns that actually yield [None]; data-driven prompt assembly from JSON configs where a field is null; conditional splices inserting a sentinel value.

Related errors


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