langchain-ai/langchain · error · ValueError

{save_path} must be json or yaml

Error message

{save_path} must be json or yaml

What it means

Raised by BaseLLM.save_to_disk (via save) when the target file's suffix is neither .json nor .yaml/.yml. The LLM configuration is serialized by inspecting the path extension, so any other extension is unsupported.

Source

Thrown at libs/core/langchain_core/language_models/llms.py:1439

        """
        # Convert file to Path object.
        save_path = Path(file_path)

        directory_path = save_path.parent
        directory_path.mkdir(parents=True, exist_ok=True)

        # Fetch dictionary to save
        prompt_dict = self._dict_for_compat()

        if save_path.suffix == ".json":
            with save_path.open("w", encoding="utf-8") as f:
                json.dump(prompt_dict, f, indent=4)
        elif save_path.suffix.endswith((".yaml", ".yml")):
            with save_path.open("w", encoding="utf-8") as f:
                yaml.dump(prompt_dict, f, default_flow_style=False)
        else:
            msg = f"{save_path} must be json or yaml"
            raise ValueError(msg)


class LLM(BaseLLM):
    """Simple interface for implementing a custom LLM.

    You should subclass this class and implement the following:

    - `_call` method: Run the LLM on the given prompt and input (used by `invoke`).
    - `_identifying_params` property: Return a dictionary of the identifying parameters
        This is critical for caching and tracing purposes. Identifying parameters
        is a dict that identifies the LLM.
        It should mostly include a `model_name`.

    Optional: Override the following methods to provide more optimizations:

    - `_acall`: Provide a native async version of the `_call` method.
        If not provided, will delegate to the synchronous version using
        `run_in_executor`. (Used by `ainvoke`).

View on GitHub (pinned to e32fa9a52e)

Solutions

  1. Rename the target file to end in .json (recommended, avoids the optional PyYAML dependency): e.g. Path('model.json')
  2. Or use .yaml / .yml if YAML output is preferred
  3. Normalize the suffix before saving: save_path = save_path.with_suffix('.json')

Example fix

# before
llm.save_to_disk(Path('model_config.cfg'))
# after
llm.save_to_disk(Path('model_config.json'))
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import Path
p = Path(save_path)
if p.suffix not in {'.json', '.yaml', '.yml'}:
    p = p.with_suffix('.json')
llm.save(p)

Type guard

def is_supported_save_path(p: 'Path') -> bool:
    return p.suffix in {'.json', '.yaml', '.yml'}

Try / catch

try:
    llm.save(save_path)
except ValueError as e:
    if 'must be json or yaml' in str(e):
        llm.save(save_path.with_suffix('.json'))
    else:
        raise

Prevention

When it happens

Trigger: llm.save_to_disk(Path('model.cfg')), llm.save_to_disk('llm.txt'), or a path with no suffix. Only '.json', '.yaml', and '.yml' are accepted (checked on save_path.suffix, so '.yml' works via the endswith check).

Common situations: Using a generic config filename like model.conf or llm.pkl; case-sensitivity surprises like '.JSON' (suffix comparison is case-sensitive); building paths dynamically without normalizing the extension.

Related errors


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