{"record":{"id":"430000a03d764e36","repo":"bytedance/deer-flow","slug":"the-source-user-message-is-missing-an-id","errorCode":null,"errorMessage":"The source user message is missing an id","messagePattern":"The source user message is missing an id","errorType":"http","errorClass":"HTTPException","httpStatus":409,"severity":"error","filePath":"backend/app/gateway/routers/thread_runs.py","lineNumber":682,"sourceCode":"        previous_human = next((message for message in reversed(messages[:target_index]) if _is_visible_human_message(message)), None)\n        target_run_id = (\n            await _find_target_run_id(\n                thread_id,\n                message_id,\n                target_message,\n                previous_human,\n                request,\n            )\n            if previous_human is not None\n            else None\n        )\n    if previous_human is None:\n        raise HTTPException(status_code=409, detail=\"Could not find the user message for this assistant response\")\n    if target_run_id is None:\n        raise HTTPException(status_code=409, detail=\"Could not find source run for assistant message\")\n    previous_human_id = _message_id(previous_human)\n    if not previous_human_id:\n        raise HTTPException(status_code=409, detail=\"The source user message is missing an id\")\n\n    base_checkpoint_tuple = await _find_base_checkpoint_before_human(\n        thread_id,\n        previous_human_id,\n        request,\n        head_checkpoint=latest_checkpoint,\n    )\n    checkpoint = _checkpoint_response(base_checkpoint_tuple)\n    metadata = {\n        \"regenerate_from_message_id\": message_id,\n        \"regenerate_from_run_id\": target_run_id,\n        \"regenerate_checkpoint_id\": checkpoint[\"checkpoint_id\"],\n    }\n    regenerate_input: dict[str, Any] = {\"messages\": [_clean_human_message_for_regenerate(previous_human)]}\n    latest_values = latest_checkpoint.values if isinstance(latest_checkpoint.values, dict) else {}\n    latest_title = latest_values.get(\"title\")\n    if isinstance(latest_title, str) and latest_title:\n        # Regenerate resumes from the checkpoint before the target human turn.","sourceCodeStart":664,"sourceCodeEnd":700,"githubUrl":"https://github.com/bytedance/deer-flow/blob/1dd6ba1acb03700589994b0366c5d1c7d05e2eff/backend/app/gateway/routers/thread_runs.py#L664-L700","documentation":"Raised in _prepare_regenerate_payload (HTTP 409) when the preceding human message exists but carries no message id. The base-checkpoint lookup keys on the human message id (find_checkpoint_before_message_chronologically), so an id-less user message makes the rewind unaddressable and regenerate is refused.","triggerScenarios":"Checkpointed state where the HumanMessage object lacks an id (older LangGraph writes did not always stamp ids); messages injected programmatically without ids; deserialization that drops the id field.","commonSituations":"Threads created before an upgrade that started stamping message ids; custom code that appends HumanMessage(id=None); middleware that rewrites messages and loses ids.","solutions":["Ensure every user message added to graph state has a stable id (LangChain message id or server-stamped).","For legacy threads without ids, continue the conversation in a new thread rather than regenerating.","Audit custom middleware/extensions that mutate checkpointed messages and preserve the id field."],"exampleFix":"# before\nstate[\"messages\"].append(HumanMessage(content=text))\n# after\nimport uuid\nstate[\"messages\"].append(HumanMessage(content=text, id=str(uuid.uuid4())))","handlingStrategy":"validation","validationCode":"const idx = messages.findIndex(m => m.id === messageId);\nconst prevHuman = messages.slice(0, idx).findLast(m => m.type === 'human' && !m.hidden);\nif (!prevHuman?.id) { disableRegenerate(); }","typeGuard":"function humanMessageHasId(m: {type: string; id?: string} | undefined): boolean {\n  return !!m && m.type === 'human' && typeof m.id === 'string' && m.id.length > 0;\n}","tryCatchPattern":"try { await regeneratePrepare(threadId, messageId); } catch (e) { if (e.status === 409 && /missing an id/.test(e.detail)) { notify('Legacy turn cannot be regenerated; start a new message'); } else throw e; }","preventionTips":["Always stamp message ids on HumanMessage","Keep middleware from stripping ids","Migrate legacy id-less threads or avoid rewind on them"],"tags":["regenerate","message-id","http-409"],"backgroundTag":null,"analyzedSha":"1dd6ba1acb03700589994b0366c5d1c7d05e2eff","analyzedAt":"2026-08-14T21:20:34.804Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}