langchain-ai/langchain · error · NotImplementedError
Message as a sequence must be (role string, template)
Error message
Message as a sequence must be (role string, template)
What it means
Raised (as `NotImplementedError`) by `convert_to_messages` when a sequence-form message cannot be unpacked into exactly two elements `(role_string, template)`. Sequences are only supported in the strict 2-tuple form; anything else (3-tuple, 1-tuple, or a non-string sequence misused) hits this.
Source
Thrown at libs/core/langchain_core/messages/utils.py:742
Returns:
An instance of a message or a message template.
Raises:
NotImplementedError: if the message type is not supported.
ValueError: if the message dict does not contain the required keys.
"""
if isinstance(message, BaseMessage):
message_ = message
elif isinstance(message, Sequence):
if isinstance(message, str):
message_ = _create_message_from_message_type("human", message)
else:
try:
message_type_str, template = message
except ValueError as e:
msg = "Message as a sequence must be (role string, template)"
raise NotImplementedError(msg) from e
message_ = _create_message_from_message_type(message_type_str, template)
elif isinstance(message, dict):
# `Serializable` constructor-envelope wire shape. Detect structurally, map
# the class name to a known message-type string via a hardcoded
# allowlist, and recurse with the canonical
# `{"type": ..., **kwargs}` shape — no `load()`, no dynamic
# class instantiation.
if (
message.get("lc") == 1
and message.get("type") == "constructor"
and isinstance(message.get("id"), list)
and message["id"]
and isinstance(message.get("kwargs"), dict)
):
mapped = _LC_CONSTRUCTOR_NAME_TO_TYPE.get(message["id"][-1])
if mapped is not None:
return _convert_to_message({"type": mapped, **message["kwargs"]})
View on GitHub (pinned to e32fa9a52e)
Solutions
- Use exactly 2-element tuples: `(role_str, content)`
- Validate tuple length before passing: `assert len(t) == 2`
- Prefer the dict form `{'type': role, 'content': content}` for anything with extra fields
Example fix
# before
convert_to_messages([('human', 'hi', {'meta': 1})])
# after
convert_to_messages([('human', 'hi')])
# extra data goes in the dict form instead:
# [{'type': 'human', 'content': 'hi', 'additional_kwargs': {'meta': 1}}] Defensive patterns
Strategy: validation
Validate before calling
def is_valid_tuple_message(m) -> bool:
return isinstance(m, tuple) and len(m) == 2 and isinstance(m[0], str) Type guard
def is_role_template_pair(m: object) -> bool:
return (isinstance(m, (tuple, list)) and len(m) == 2
and isinstance(m[0], str) and isinstance(m[1], (str, list))) Try / catch
try:
msgs = convert_to_messages(raw)
except NotImplementedError as e:
if 'sequence must be (role string, template)' in str(e):
raw = [(r, c) for r, c, *_ in (m if len(m) > 2 else (*m, None) for m in raw if isinstance(m, (tuple, list)))]
msgs = convert_to_messages(raw)
else:
raise Prevention
- Standardize on 2-tuples when building message tuples
- Prefer dict form for messages carrying extra fields
- Assert tuple shape in tests for message-building helpers
When it happens
Trigger: Passing `('human', 'hi', 'extra')`, `('human',)`, or a list whose unpacking raises `ValueError` inside the `message_type_str, template = message` statement.
Common situations: Building message tuples programmatically and accidentally appending extra fields; passing a 3-element tuple intended for another API; a list of characters when a string was expected (though bare strings take the human path).
Related errors
- MESSAGE_COERCION_FAILURE
- Invalid input type {type(model_input)}. Must be a PromptValu
- Received unsupported arguments {kwargs}
- Unsupported cache value {cache}
- Invalid input type {type(model_input)}. Must be a PromptValu
AI-assisted analysis of langchain-ai/langchain@e32fa9a52e (2026-08-14).
Data as JSON: /api/errors/89240d5ca0a4e94c.
Report an issue: GitHub.