bytedance/deer-flow · error · HTTPException
Invalid message at input.messages[{index}]: {exc}
Error message
Invalid message at input.messages[{index}]: {exc} What it means
HTTP 400 raised while converting the input.messages array of a run request: a dict-form message failed conversion in convert_to_messages (ValueError/TypeError/NotImplementedError). The detail includes the failing index and the converter's reason, so the offending message can be located precisely. Conversion happens before any run starts, so the request is rejected cleanly.
Source
Thrown at backend/app/gateway/services.py:270
``original_user_content``, dynamic-context reminder markers, and the
transient view-image context marker are server-owned. External callers
cannot supply them; trusted internal channel calls may preserve metadata
they added before invoking this boundary.
"""
if raw_input is None:
return {}
messages = raw_input.get("messages")
if messages and isinstance(messages, list):
converted: list[Any] = []
for index, msg in enumerate(messages):
if isinstance(msg, BaseMessage):
converted.append(msg)
elif isinstance(msg, dict):
try:
converted.extend(convert_to_messages([msg]))
except (ValueError, TypeError, NotImplementedError) as exc:
raise HTTPException(
status_code=400,
detail=f"Invalid message at input.messages[{index}]: {exc}",
) from exc
else:
converted.append(msg)
if not trusted_internal:
converted = [_strip_external_message_metadata(message) for message in converted]
return {**raw_input, "messages": converted}
return raw_input
_DEFAULT_ASSISTANT_ID = "lead_agent"
# Whitelist of run-context keys that the langgraph-compat layer forwards from
# ``body.context`` into the run config. ``config["context"]`` exists in
# LangGraph >=0.6, but these values must be written to both ``configurable``
# (for legacy ``_get_runtime_config`` consumers) and ``context`` becauseView on GitHub (pinned to 1dd6ba1acb)
Solutions
- Use the index from the error (input.messages[N]) to find the exact bad message.
- Fix that dict to a supported shape: include a valid 'type'/'role', well-formed content, and required ids for tool messages.
- Prefer sending messages already shaped like LangChain BaseMessage serializations, or validate with the same convert_to_messages helper client-side if embedded.
Example fix
# before
{"input": {"messages": [{"role": "user", "content": "hi"}, {"type": "tool_result", "content": "42"}]}}
# after
{"input": {"messages": [{"role": "user", "content": "hi"}, {"type": "tool", "tool_call_id": "call_1", "content": "42"}]}} Defensive patterns
Strategy: type-guard
Validate before calling
const VALID_TYPES = new Set(['human', 'ai', 'system', 'tool', 'user', 'assistant']);
function messagesAreValid(msgs) {
return msgs.every((m, i) => {
if (typeof m !== 'object' || m === null) return true; // passthrough
if (!VALID_TYPES.has(m.type ?? m.role)) { console.error(`messages[${i}] has bad type`); return false; }
if ((m.type ?? m.role) === 'tool' && !m.tool_call_id) { console.error(`messages[${i}] tool missing tool_call_id`); return false; }
return true;
});
} Type guard
function isMessageDict(m: unknown): m is Record<string, unknown> {
if (typeof m !== 'object' || m === null) return false;
const t = (m as any).type ?? (m as any).role;
return typeof t === 'string' && VALID_TYPES.has(t);
} Try / catch
catch 400 'Invalid message at input.messages[N]'; use N and the exc text to fix that one message, then resend.
Prevention
- Build messages with a typed client helper instead of raw dicts.
- Add a payload schema check in client tests covering every message type you send.
- Keep tool messages paired with their tool_call_id from the moment you capture them.
When it happens
Trigger: POSTing a run whose input.messages[i] dict has an unknown/missing 'type', malformed role fields, or a message shape convert_to_messages does not accept (e.g. a tool message without a matching tool_call_id, or unsupported content blocks).
Common situations: Replaying captured LangGraph payloads with older/newer message schemas; hand-building message dicts; sending provider-specific content shapes (Anthropic/OpenAI blocks) where DeerFlow expects its own schema; BaseMessage objects pass through, only dicts are converted.
Related errors
AI-assisted analysis of bytedance/deer-flow@1dd6ba1acb (2026-08-14).
Data as JSON: /api/errors/ff493a32ce93f68b.
Report an issue: GitHub.