{"record":{"id":"010af63d6fad2a53","repo":"langchain-ai/langchain","slug":"save-path-must-be-json-or-yaml-010af6","errorCode":null,"errorMessage":"{save_path} must be json or yaml","messagePattern":"(.+?) must be json or yaml","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"libs/core/langchain_core/prompts/base.py","lineNumber":429,"sourceCode":"            msg = f\"Prompt {self} does not support saving.\"\n            raise NotImplementedError(msg)\n\n        # Convert file to Path object.\n        save_path = Path(file_path)\n\n        directory_path = save_path.parent\n        directory_path.mkdir(parents=True, exist_ok=True)\n\n        resolved_path = save_path.resolve()\n        if resolved_path.suffix == \".json\":\n            with resolved_path.open(\"w\", encoding=\"utf-8\") as f:\n                json.dump(prompt_dict, f, indent=4)\n        elif resolved_path.suffix.endswith((\".yaml\", \".yml\")):\n            with resolved_path.open(\"w\", encoding=\"utf-8\") as f:\n                yaml.dump(prompt_dict, f, default_flow_style=False)\n        else:\n            msg = f\"{save_path} must be json or yaml\"\n            raise ValueError(msg)\n\n\ndef _get_document_info(\n    doc: Document, prompt: BasePromptTemplate[str]\n) -> dict[str, Any]:\n    base_info = {\"page_content\": doc.page_content, **doc.metadata}\n    missing_metadata = set(prompt.input_variables).difference(base_info)\n    if len(missing_metadata) > 0:\n        required_metadata = [\n            iv for iv in prompt.input_variables if iv != \"page_content\"\n        ]\n        msg = (\n            f\"Document prompt requires documents to have metadata variables: \"\n            f\"{required_metadata}. Received document with missing metadata: \"\n            f\"{list(missing_metadata)}.\"\n        )\n        raise ValueError(\n            create_message(message=msg, error_code=ErrorCode.INVALID_PROMPT_INPUT)","sourceCodeStart":411,"sourceCodeEnd":447,"githubUrl":"https://github.com/langchain-ai/langchain/blob/e32fa9a52eab3b61ad7a45399bfde59b3e580fc4/libs/core/langchain_core/prompts/base.py#L411-L447","documentation":"save() only supports JSON and YAML destinations. After resolving the path, if the suffix is neither .json nor .yaml/.yml, it raises ValueError('<path> must be json or yaml'). The extension is the format selector, so unrecognized or missing extensions are rejected before any file is written (note the parent directory is created first).","triggerScenarios":"Calling prompt.save('prompt.txt'), prompt.save('prompts/prompt') with no extension, or passing a path whose casing/format token is not exactly .json/.yaml/.yml.","commonSituations":"Exporting prompts into existing artifact pipelines that expect .txt/.md/.toml; typos like '.yamal'; or dynamically built filenames that drop the extension.","solutions":["Rename the destination to end in .json, .yaml, or .yml","If you need another format, save to .json first and convert with your own tooling"],"exampleFix":"# before\nprompt.save(\"prompts/instructions.txt\")  # ValueError\n\n# after\nprompt.save(\"prompts/instructions.yaml\")","handlingStrategy":"validation","validationCode":"from pathlib import Path\n\ndef valid_save_path(p: str) -> bool:\n    return Path(p).suffix in {\".json\", \".yaml\", \".yml\"}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Centralize prompt-export paths through a helper that enforces the .json/.yaml suffix","Watch for missing extensions when filenames are built dynamically"],"tags":["prompts","serialization","file-format","core"],"backgroundTag":null,"analyzedSha":"e32fa9a52eab3b61ad7a45399bfde59b3e580fc4","analyzedAt":"2026-08-14T18:42:09.092Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}