langchain-ai/langchain · error · ValueError

Missing keys {sorted(missing_keys)} in config['configurable'

Error message

Missing keys {sorted(missing_keys)} in config['configurable'] Expected keys are {sorted(expected_keys)}.When using via .invoke() or .stream(), pass in a config; e.g., chain.invoke({example_input}, {example_config})

What it means

RunnableWithMessageHistory derives session-scoped history by calling your get_session_history callable with keys from config['configurable']. Before invoking, it computes which expected keys are missing from the configurable dict; any missing key (with a parameterized callable) raises this ValueError, complete with an example of how to pass config to .invoke()/.stream().

Source

Thrown at libs/core/langchain_core/runnables/history.py:600

        config = super()._merge_configs(*configs)
        expected_keys = [field_spec.id for field_spec in self.history_factory_config]

        configurable = config.get("configurable", {})

        missing_keys = set(expected_keys) - set(configurable.keys())
        parameter_names = _get_parameter_names(self.get_session_history)

        if missing_keys and parameter_names:
            example_input = {self.input_messages_key: "foo"}
            example_configurable = dict.fromkeys(missing_keys, "[your-value-here]")
            example_config = {"configurable": example_configurable}
            msg = (
                f"Missing keys {sorted(missing_keys)} in config['configurable'] "
                f"Expected keys are {sorted(expected_keys)}."
                f"When using via .invoke() or .stream(), pass in a config; "
                f"e.g., chain.invoke({example_input}, {example_config})"
            )
            raise ValueError(msg)

        if len(expected_keys) == 1:
            if parameter_names:
                # If arity = 1, then invoke function by positional arguments
                message_history = self.get_session_history(
                    configurable[expected_keys[0]]
                )
            else:
                if not config:
                    config["configurable"] = {}
                message_history = self.get_session_history()
        else:
            # otherwise verify that names of keys patch and invoke by named arguments
            if set(expected_keys) != set(parameter_names):
                msg = (
                    f"Expected keys {sorted(expected_keys)} do not match parameter "
                    f"names {sorted(parameter_names)} of get_session_history."
                )

View on GitHub (pinned to e32fa9a52e)

Solutions

  1. Pass the session config: wrapped.invoke(input, {"configurable": {"session_id": "<id>"}})
  2. If get_session_history takes multiple params, supply all declared keys in configurable (match history_factory_config field ids)
  3. Double-check spelling: the key must equal the parameter name / field_spec.id exactly

Example fix

# before
wrapped.invoke({"question": "hi"})  # ValueError
# after
wrapped.invoke(
    {"question": "hi"}, {"configurable": {"session_id": "abc123"}},
)
Defensive patterns

Strategy: validation

Validate before calling

cfg = {"configurable": {"session_id": "abc"}}
missing = {"session_id"} - set(cfg["configurable"])
assert not missing, f"pass configurable with {missing}"

Try / catch

try:
    wrapped.invoke(x, cfg)
except ValueError as e:
    if "Missing keys" in str(e):
        wrapped.invoke(x, {"configurable": {"session_id": new_session_id()}})

Prevention

When it happens

Trigger: Calling wrapped.invoke(input) or .stream() without {"configurable": {"session_id": "..."}} (or without whichever keys get_session_history declares, e.g. user_id when using history_factory_config).

Common situations: Forgetting the config argument; passing config=None; renaming configurable keys so they no longer match the factory signature; async paths (.ainvoke) hitting the same merge.

Related errors


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