langchain-ai/langgraph · error · ValueError
is not a valid interleave_projections channel . Supported…
Error message
{ch!r} is not a valid interleave_projections channel{hint}. Supported channels: {', '.join(SUPPORTED_INTERLEAVE_CHANNELS)}, or an extension name. What it means
Fail-closed channel validation before stream setup: 'lifecycle', 'tools' and 'input' are reserved protocol channel names. If allowed through, they would fall into the extension-name/'custom:' fallback, subscribe to a channel that never matches, and silently yield nothing, so validate_interleave_channels rejects them up front with a hint about supported channels.
Solutions
- Use the public wire name 'tool_calls' instead of the reserved alias 'tools'
- Use a supported channel name or a real extension name / 'custom:<name>' channel
- Do not subscribe to control-plane channels ('lifecycle', 'input') via interleave; they are managed by the protocol itself
Defensive patterns
Strategy: validation
When it happens
Trigger: Thrown at libs/sdk-py/langgraph_sdk/stream/decoders.py:45 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of langchain-ai/langgraph@38031739e5 (2026-08-26).
Data as JSON: /api/errors/4e4bce1d1b75edcd.
Report an issue: GitHub.
Appendix: source
Thrown at libs/sdk-py/langgraph_sdk/stream/decoders.py:45
#: to the extension/``custom:`` fallback would subscribe to a channel that never
#: matches and silently yield nothing, so they are rejected up front (fail
#: closed). ``lifecycle`` is control-plane (drives run output/interrupt); ``tools``
#: is the wire alias for the public ``tool_calls`` channel.
RESERVED_INTERLEAVE_CHANNELS = frozenset({"lifecycle", "tools", "input"})
def validate_interleave_channels(channels: list[str]) -> None:
"""Reject reserved protocol channel names before they hit the fallback.
Genuine extension names pass through untouched; only names that
``infer_channel`` treats as built-in methods without an interleave decoder
are rejected, so a typo'd or unsupported protocol channel surfaces an error
instead of an empty stream.
"""
for ch in channels:
if ch in RESERVED_INTERLEAVE_CHANNELS:
hint = ' (use "tool_calls")' if ch == "tools" else ""
raise ValueError(
f"{ch!r} is not a valid interleave_projections channel{hint}. "
f"Supported channels: {', '.join(SUPPORTED_INTERLEAVE_CHANNELS)}, "
"or an extension name."
)
def _event_namespace(params_field: Any) -> list[str]:
if not isinstance(params_field, dict):
return []
namespace = params_field.get("namespace") or []
return list(namespace) if isinstance(namespace, list) else []
def _message_event_id(data: dict[str, Any]) -> str | None:
message_id = data.get("id") or data.get("message_id")
return str(message_id) if message_id is not None else None
View on GitHub (pinned to 38031739e5)