{"record":{"id":"db34ce6f4f7c97ea","repo":"langchain-ai/langchain","slug":"jinja2-templates-are-not-allowed-during-deserializ","errorCode":null,"errorMessage":"Jinja2 templates are not allowed during deserialization for security reasons. Use 'f-string' template format instead, or explicitly allow jinja2 by providing a custom init_validator.","messagePattern":"Jinja2 templates are not allowed during deserialization for security reasons\\. Use 'f-string' template format instead, or explicitly allow jinja2 by providing a custom init_validator\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"critical","filePath":"libs/core/langchain_core/load/load.py","lineNumber":248,"sourceCode":"        We intentionally do NOT check the `class_path` here to keep this simple and\n        future-proof. If any new class is added that accepts `template_format='jinja2'`,\n        it will be automatically blocked without needing to update this function.\n\n    Args:\n        class_path: The class path tuple being deserialized (unused).\n        kwargs: The kwargs dict for the class constructor.\n\n    Raises:\n        ValueError: If `template_format` is `'jinja2'`.\n    \"\"\"\n    _ = class_path  # Unused - see docstring for rationale. Kept to satisfy signature.\n    if kwargs.get(\"template_format\") == \"jinja2\":\n        msg = (\n            \"Jinja2 templates are not allowed during deserialization for security \"\n            \"reasons. Use 'f-string' template format instead, or explicitly allow \"\n            \"jinja2 by providing a custom init_validator.\"\n        )\n        raise ValueError(msg)\n\n\ndef default_init_validator(\n    class_path: tuple[str, ...],\n    kwargs: dict[str, Any],\n) -> None:\n    \"\"\"Default init validator that blocks jinja2 templates.\n\n    This is the default validator used by `load()` and `loads()` when no custom\n    validator is provided.\n\n    Args:\n        class_path: The class path tuple being deserialized.\n        kwargs: The kwargs dict for the class constructor.\n\n    Raises:\n        ValueError: If template_format is `'jinja2'`.\n    \"\"\"","sourceCodeStart":230,"sourceCodeEnd":266,"githubUrl":"https://github.com/langchain-ai/langchain/blob/e32fa9a52eab3b61ad7a45399bfde59b3e580fc4/libs/core/langchain_core/load/load.py#L230-L266","documentation":"Security guard raised during deserialization when a serialized PromptTemplate (or similar) has template_format='jinja2'. Jinja2 template rendering can execute arbitrary expressions, so loading untrusted serialized payloads with jinja2 is blocked by default; the init_validator raises before the class is even imported.","triggerScenarios":"loads(serialized_json) where the payload contains kwargs with \"template_format\": \"jinja2\" — e.g. a chain serialized by an older LangChain version that defaulted to jinja2 templates. Also triggered by hand-crafted payloads targeting PromptTemplate.","commonSituations":"Loading legacy serialized chains from LangChain <0.0.250-ish versions where jinja2 was a common default; restoring snapshots/dumps from other teams; prompt-injection-style malicious payloads from untrusted storage.","solutions":["Re-create the template with f-string format and re-serialize: PromptTemplate.from_template(t, template_format='f-string')","If the payload is fully trusted, supply a custom init_validator to loads/loads that permits jinja2","Migrate stored workflows once, then persist the f-string versions"],"exampleFix":"# before\nobj = loads(legacy_payload)  # payload has template_format='jinja2'\n# after\nfrom langchain_core.load.load import default_init_validator\ndef allow_jinja2(class_path, kwargs): pass  # trusted payloads only\nobj = loads(legacy_payload, init_validator=allow_jinja2)","handlingStrategy":"validation","validationCode":"import json\npayload = json.loads(text)\ndef scan_for_jinja2(node):\n    if isinstance(node, dict):\n        if node.get('kwargs', {}).get('template_format') == 'jinja2' if isinstance(node.get('kwargs'), dict) else False:\n            return True\n        return any(scan_for_jinja2(v) for v in node.values())\n    if isinstance(node, list):\n        return any(scan_for_jinja2(v) for v in node)\n    return False\nif scan_for_jinja2(payload):\n    raise ValueError('payload contains jinja2 templates; migrate or supply custom init_validator')","typeGuard":null,"tryCatchPattern":"try:\n    obj = loads(text)\nexcept ValueError as e:\n    if 'Jinja2' in str(e) and payload_is_trusted:\n        obj = loads(text, init_validator=lambda cp, kw: None)\n    else:\n        raise","preventionTips":["Default new templates to template_format='f-string'","Only override init_validator for payloads whose provenance you fully control","Scan untrusted payloads for template_format='jinja2' before loading"],"tags":["security","deserialization","jinja2","prompt-template"],"backgroundTag":null,"analyzedSha":"e32fa9a52eab3b61ad7a45399bfde59b3e580fc4","analyzedAt":"2026-08-14T18:42:09.092Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}