Skip to content

DictPromptTemplate silently drops non-str/non-dict values nested in lists #39152

Description

Submission checklist

  • This is a bug, not a usage question.
  • I added a clear and descriptive title that summarizes this issue.
  • I used the GitHub search to find a similar question and didn't find it.
  • I am sure that this is a bug in LangChain rather than my code.
  • The bug is not resolved by updating to the latest stable version of LangChain (or the specific integration package).
  • This is not related to the langchain-community package.
  • I posted a self-contained, minimal, reproducible example. A maintainer can copy it and run it AS IS.

Package (Required)

  • langchain
  • langchain-openai
  • langchain-anthropic
  • langchain-classic
  • langchain-core
  • langchain-model-profiles
  • langchain-tests
  • langchain-text-splitters
  • langchain-chroma
  • langchain-deepseek
  • langchain-exa
  • langchain-fireworks
  • langchain-groq
  • langchain-huggingface
  • langchain-mistralai
  • langchain-nomic
  • langchain-ollama
  • langchain-openrouter
  • langchain-perplexity
  • langchain-qdrant
  • langchain-xai
  • Other / not sure / general

Related Issues / PRs

No response

Reproduction Steps / Example Code (Python)

from langchain_core.prompts import ChatPromptTemplate, DictPromptTemplate

# 1. Direct use of DictPromptTemplate
template = {
    "type": "tool_use",
    "id": "call_1",
    "name": "search",
    "input": {"query": "{q}", "top_k_scores": [1, 2, 3], "flags": [True, None]},
}
prompt = DictPromptTemplate(template=template, template_format="f-string")
print("1 formatted :", prompt.format(q="cats"))

# 2. Same loss through the ChatPromptTemplate public API
chat = ChatPromptTemplate.from_messages(
    [("ai", [{"type": "text", "text": "{q}", "nums": [1, 2, 3]}])]
)
print("2 content   :", chat.format_messages(q="hello")[0].content)

# 3. Mixed list: str/dict survive, everything else disappears
p3 = DictPromptTemplate(
    template={"type": "x", "mixed": ["a", 1, 2.5, {"k": "v"}, None, True]},
    template_format="f-string",
)
print("3 mixed     :", p3.format())

# 4. The same scalars are preserved when they are NOT inside a list
p4 = DictPromptTemplate(
    template={"type": "x", "scalar": 1, "nested": {"scalar": 2}},
    template_format="f-string",
)
print("4 scalars   :", p4.format())

# 5. Data is destroyed even when the template has no variables at all
p5 = DictPromptTemplate(template={"nums": [1, 2]}, template_format="mustache")
print("5 no vars   :", p5.format())

Error Message and Stack Trace (if applicable)

Description

DictPromptTemplate.format() is expected to substitute the template variables and return the template otherwise unchanged. Instead, any item nested inside a list or tuple that is not a str or a dict is silently deleted from the output: int, float, bool, None, and nested lists all disappear.

Actual output of the snippet above (langchain-core 1.5.3, also reproduced from master source at a1a1ad3):

1 formatted : {'type': 'tool_use', 'id': 'call_1', 'name': 'search', 'input': {'query': 'cats', 'top_k_scores': [], 'flags': []}}
2 content   : [{'type': 'text', 'text': 'hello', 'nums': []}]
3 mixed     : {'type': 'x', 'mixed': ['a', {'k': 'v'}]}
4 scalars   : {'type': 'x', 'scalar': 1, 'nested': {'scalar': 2}}
5 no vars   : {'nums': []}

Expected output:

1 formatted : {'type': 'tool_use', 'id': 'call_1', 'name': 'search', 'input': {'query': 'cats', 'top_k_scores': [1, 2, 3], 'flags': [True, None]}}
2 content   : [{'type': 'text', 'text': 'hello', 'nums': [1, 2, 3]}]
3 mixed     : {'type': 'x', 'mixed': ['a', 1, 2.5, {'k': 'v'}, None, True]}
4 scalars   : {'type': 'x', 'scalar': 1, 'nested': {'scalar': 2}}
5 no vars   : {'nums': [1, 2]}

Root cause

libs/core/langchain_core/prompts/dict.py, _insert_input_variables (lines 143-175). The list/tuple branch has no fallback for items that are neither str nor dict, so they are never appended to formatted_v:

elif isinstance(v, (list, tuple)):
    formatted_v: list[str | dict[str, Any]] = []
    for x in v:
        if isinstance(x, str):
            formatted_v.append(formatter(x, **inputs))
        elif isinstance(x, dict):
            formatted_v.append(
                _insert_input_variables(x, inputs, template_format)
            )
    formatted[k] = type(v)(formatted_v)   # non-str / non-dict items are gone
else:
    formatted[k] = v                      # ... but scalars outside a list are preserved

Line 174 keeps 1 for {"scalar": 1}, while line 172 drops the same 1 for {"nums": [1]}, so the handling is inconsistent within the same function.

Why it matters

DictPromptTemplate is public (from langchain_core.prompts import DictPromptTemplate) and ChatPromptTemplate.from_messages routes every dict content block through it (prompts/chat.py, lines 516-527, formatted at 612-615 / 643-646). Any templated message whose content block carries a list of numbers or booleans loses that data before it reaches the model. Realistic cases: few-shot examples containing provider tool-use blocks such as {"type": "tool_use", ..., "input": {"ids": [1, 2, 3]}}, or blocks carrying scores, page numbers, logprobs, or image dimensions. The model silently receives an empty list.

Case 5 shows the values are destroyed even when the template contains no variables, so no formatting work justifies the mutation.

The class docstring says variables are recognized in string dict values and that this "applies recursively"; it does not document any pruning. format/aformat are documented to return "A formatted dict", and the type contract is dict[str, Any] -> dict[str, Any], so Any values pass construction and validation and are then discarded at format time.

Test coverage

libs/core/tests/unit_tests/prompts/test_dict.py never places a list (or any non-str/non-dict value) in a template, so this path is uncovered. test_chat.py has no equivalent case either.

Possible fix

Add a pass-through fallback in the list branch (widening formatted_v to list[Any]), and recurse for nested lists so they are handled like top-level ones:

else:
    formatted_v.append(x)

Happy to open a PR with the fix plus regression tests if maintainers agree with the direction.

System Info

System Information

OS: Windows
OS Version: 10.0.26200
Python Version: 3.11.9 (tags/v3.11.9:de54cf5, Apr 2 2024, 10:12:12) [MSC v.1938 64 bit (AMD64)]

Package Information

langchain_core: 1.5.3
langsmith: 0.10.13
langchain_text_splitters: 1.1.2

Other Dependencies

pydantic: 2.13.4
typing-extensions: 4.16.0

Also confirmed against master source at commit a1a1ad3 (libs/core/langchain_core/prompts/dict.py).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugRelated to a bug, vulnerability, unexpected error with an existing featurecore`langchain-core` package issues & PRsexternal

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions