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
- Pass the session config: wrapped.invoke(input, {"configurable": {"session_id": "<id>"}})
- If get_session_history takes multiple params, supply all declared keys in configurable (match history_factory_config field ids)
- 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
- Standardize a helper make_config(session_id) used for every history-wrapped call
- Never call .invoke()/.stream() on these wrappers without a configurable dict
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
- Expected keys {sorted(expected_keys)} do not match parameter
- Expected a single list of messages. Got {input_val}.
- Expected str, BaseMessage, list[BaseMessage], or tuple[BaseM
- Expected str, BaseMessage, list[BaseMessage], or tuple[BaseM
- Loading {config_type} prompt not supported
AI-assisted analysis of langchain-ai/langchain@e32fa9a52e (2026-08-14).
Data as JSON: /api/errors/5171597288a7892b.
Report an issue: GitHub.