langchain-ai/langchain · error · ValueError

Expected keys {sorted(expected_keys)} do not match parameter

Error message

Expected keys {sorted(expected_keys)} do not match parameter names {sorted(parameter_names)} of get_session_history.

What it means

When more than one key is expected (multi-parameter get_session_history or multiple history_factory_config fields), RunnableWithMessageHistory invokes the callable by keyword arguments and requires that the set of expected keys exactly equals the set of parameter names in get_session_history's signature. Any mismatch raises this ValueError listing both sets.

Source

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

        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."
                )
                raise ValueError(msg)

            message_history = self.get_session_history(
                **{key: configurable[key] for key in expected_keys}
            )
        config["configurable"]["message_history"] = message_history
        return config


def _get_parameter_names(callable_: GetSessionHistoryCallable) -> list[str]:
    """Get the parameter names of the `Callable`."""
    sig = inspect.signature(callable_)
    return list(sig.parameters.keys())

View on GitHub (pinned to e32fa9a52e)

Solutions

  1. Align the ConfigurableFieldSpec ids with the get_session_history parameter names exactly (or vice versa)
  2. Simplify: if possible use a single-parameter factory taking just session_id
  3. Verify with inspect.signature(get_session_history) and compare against the field ids you pass to history_factory_config

Example fix

# before
def get_history(session_id: str, tenant: str): ...
history_factory_config=[
    ConfigurableFieldSpec(id="session_id", ...),
    ConfigurableFieldSpec(id="tenant_id", ...),  # mismatch: param is `tenant`
]
# after
ConfigurableFieldSpec(id="tenant", ...)  # matches parameter name
Defensive patterns

Strategy: validation

Validate before calling

import inspect
params = set(inspect.signature(get_session_history).parameters)
expected = {f.id for f in history_factory_config}
assert params == expected, f"{params} != {expected}"

Try / catch

try:
    wrapped.invoke(x, cfg)
except ValueError as e:
    if "do not match parameter" in str(e):
        fix_field_ids_to_match_signature()

Prevention

When it happens

Trigger: A get_session_history(session_id, user_id) factory but history_factory_config declaring fields named differently (e.g. session_id, tenant_id); declaring two ConfigurableFieldSpec ids that do not match the function parameters.

Common situations: Multi-tenant history stores keyed by (user_id, session_id); refactoring parameter names without updating ConfigurableFieldSpec ids; copying a factory from docs with different naming.

Related errors


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