{"record":{"id":"082bb719ae1739ae","repo":"langchain-ai/langchain","slug":"runnablesequence-contains-conflicting-config-specs","errorCode":null,"errorMessage":"RunnableSequence contains conflicting config specsfor {spec_id}: {[first, *others]}","messagePattern":"RunnableSequence contains conflicting config specsfor (.+?): (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"libs/core/langchain_core/runnables/utils.py","lineNumber":708,"sourceCode":"\n    Raises:\n        ValueError: If the runnable sequence contains conflicting config specs.\n    \"\"\"\n    grouped = groupby(\n        sorted(specs, key=lambda s: (s.id, *(s.dependencies or []))), lambda s: s.id\n    )\n    unique: list[ConfigurableFieldSpec] = []\n    for spec_id, dupes in grouped:\n        first = next(dupes)\n        others = list(dupes)\n        if len(others) == 0 or all(o == first for o in others):\n            unique.append(first)\n        else:\n            msg = (\n                \"RunnableSequence contains conflicting config specs\"\n                f\"for {spec_id}: {[first, *others]}\"\n            )\n            raise ValueError(msg)\n    return unique\n\n\nclass _RootEventFilter:\n    def __init__(\n        self,\n        *,\n        include_names: Sequence[str] | None = None,\n        include_types: Sequence[str] | None = None,\n        include_tags: Sequence[str] | None = None,\n        exclude_names: Sequence[str] | None = None,\n        exclude_types: Sequence[str] | None = None,\n        exclude_tags: Sequence[str] | None = None,\n    ) -> None:\n        \"\"\"Utility to filter the root event in the astream_events implementation.\n\n        This is simply binding the arguments to the namespace to make save on\n        a bit of typing in the astream_events implementation.","sourceCodeStart":690,"sourceCodeEnd":726,"githubUrl":"https://github.com/langchain-ai/langchain/blob/e32fa9a52eab3b61ad7a45399bfde59b3e580fc4/libs/core/langchain_core/runnables/utils.py#L690-L726","documentation":"When collecting `configurable_fields` specs for a `RunnableSequence`, langchain groups specs by `id` and requires all specs sharing an id to be equal. If two steps in the sequence declare a configurable field with the same id but different definitions (different options/annotations), this ValueError is raised. Note the message itself is missing a space ('specsfor') — a cosmetic bug in the f-string.","triggerScenarios":"Composing a sequence where two runnables each call `ConfigurableField(id='model', ...)` with different `options`, `name`, or `default` values — e.g. building `prompt | llm.with_configurable_fields(model=...) | llm2.with_configurable_fields(model=...)` with mismatched field definitions.","commonSituations":"Reusing a configurable-field id across two partner models (e.g. an OpenAI and an Anthropic chat both configurable as `model`) inside one sequence; copy-pasting `with_configurable_fields` blocks and editing only one; building `.with_config(configurable={...})` chains after a refactor changed one field's options.","solutions":["Give each field a globally unique id per definition (e.g. 'chat_model' vs 'fallback_model') so no id conflict occurs.","If both steps must share one id, make the `ConfigurableFieldSpec` definitions identical (same options, annotation, default, name).","Inspect `chain.config_schema()` / `chain.configurable_fields()` to see the colliding specs before wiring `.with_config`."],"exampleFix":"# before\nllm_a = chat_a.with_configurable_fields(model=ConfigurableField(id='model', options={'gpt': ..., 'gpt4': ...}))\nllm_b = chat_b.with_configurable_fields(model=ConfigurableField(id='model', options={'haiku': ..., 'sonnet': ...}))\nseq = llm_a | llm_b  # conflicting spec id 'model'\n# after\nllm_b = chat_b.with_configurable_fields(model=ConfigurableField(id='fallback_model', options={'haiku': ..., 'sonnet': ...}))\nseq = llm_a | llm_b","handlingStrategy":"validation","validationCode":"from collections import defaultdict\n\ndef spec_ids_unique(seq_steps) -> bool:\n    seen = defaultdict(list)\n    for step in seq_steps:\n        for spec in getattr(step, 'config_specs', lambda: [])():\n            seen[spec.id].append(spec)\n    for sid, specs in seen.items():\n        if len({repr(s) for s in specs}) > 1:\n            return False  # same id, differing definitions -> will raise\n    return True\n\nassert spec_ids_unique(seq.steps)","typeGuard":null,"tryCatchPattern":"try:\n    final = seq.with_config(configurable={'model': 'gpt'})\nexcept ValueError as e:\n    if 'conflicting config specs' in str(e):\n        # rename the colliding field id on one step and rebuild\n        rebuild_with_unique_ids()\n    else:\n        raise","preventionTips":["Use a unique ConfigurableField id per definition across the whole sequence.","Identical ids must have byte-identical spec definitions.","Inspect chain.configurable_fields() when composing configurable chains."],"tags":["configurable-fields","runnable-sequence","lcel","config-conflict"],"backgroundTag":null,"analyzedSha":"e32fa9a52eab3b61ad7a45399bfde59b3e580fc4","analyzedAt":"2026-08-14T18:42:09.092Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}