{"record":{"id":"ff493a32ce93f68b","repo":"bytedance/deer-flow","slug":"invalid-message-at-input-messages-index-exc","errorCode":null,"errorMessage":"Invalid message at input.messages[{index}]: {exc}","messagePattern":"Invalid message at input\\.messages\\[(.+?)\\]: (.+?)","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"backend/app/gateway/services.py","lineNumber":270,"sourceCode":"\n    ``original_user_content``, dynamic-context reminder markers, and the\n    transient view-image context marker are server-owned. External callers\n    cannot supply them; trusted internal channel calls may preserve metadata\n    they added before invoking this boundary.\n    \"\"\"\n    if raw_input is None:\n        return {}\n    messages = raw_input.get(\"messages\")\n    if messages and isinstance(messages, list):\n        converted: list[Any] = []\n        for index, msg in enumerate(messages):\n            if isinstance(msg, BaseMessage):\n                converted.append(msg)\n            elif isinstance(msg, dict):\n                try:\n                    converted.extend(convert_to_messages([msg]))\n                except (ValueError, TypeError, NotImplementedError) as exc:\n                    raise HTTPException(\n                        status_code=400,\n                        detail=f\"Invalid message at input.messages[{index}]: {exc}\",\n                    ) from exc\n            else:\n                converted.append(msg)\n        if not trusted_internal:\n            converted = [_strip_external_message_metadata(message) for message in converted]\n        return {**raw_input, \"messages\": converted}\n    return raw_input\n\n\n_DEFAULT_ASSISTANT_ID = \"lead_agent\"\n\n\n# Whitelist of run-context keys that the langgraph-compat layer forwards from\n# ``body.context`` into the run config. ``config[\"context\"]`` exists in\n# LangGraph >=0.6, but these values must be written to both ``configurable``\n# (for legacy ``_get_runtime_config`` consumers) and ``context`` because","sourceCodeStart":252,"sourceCodeEnd":288,"githubUrl":"https://github.com/bytedance/deer-flow/blob/1dd6ba1acb03700589994b0366c5d1c7d05e2eff/backend/app/gateway/services.py#L252-L288","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"# before\n{\"input\": {\"messages\": [{\"role\": \"user\", \"content\": \"hi\"}, {\"type\": \"tool_result\", \"content\": \"42\"}]}}\n\n# after\n{\"input\": {\"messages\": [{\"role\": \"user\", \"content\": \"hi\"}, {\"type\": \"tool\", \"tool_call_id\": \"call_1\", \"content\": \"42\"}]}}","handlingStrategy":"type-guard","validationCode":"const VALID_TYPES = new Set(['human', 'ai', 'system', 'tool', 'user', 'assistant']);\nfunction messagesAreValid(msgs) {\n  return msgs.every((m, i) => {\n    if (typeof m !== 'object' || m === null) return true; // passthrough\n    if (!VALID_TYPES.has(m.type ?? m.role)) { console.error(`messages[${i}] has bad type`); return false; }\n    if ((m.type ?? m.role) === 'tool' && !m.tool_call_id) { console.error(`messages[${i}] tool missing tool_call_id`); return false; }\n    return true;\n  });\n}","typeGuard":"function isMessageDict(m: unknown): m is Record<string, unknown> {\n  if (typeof m !== 'object' || m === null) return false;\n  const t = (m as any).type ?? (m as any).role;\n  return typeof t === 'string' && VALID_TYPES.has(t);\n}","tryCatchPattern":"catch 400 'Invalid message at input.messages[N]'; use N and the exc text to fix that one message, then resend.","preventionTips":["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."],"tags":["runs","http-400","messages","validation"],"backgroundTag":null,"analyzedSha":"1dd6ba1acb03700589994b0366c5d1c7d05e2eff","analyzedAt":"2026-08-14T21:20:34.804Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}