bytedance/deer-flow · error · HTTPException

The source user message is missing an id

Error message

The source user message is missing an id

What it means

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.

Source

Thrown at backend/app/gateway/routers/thread_runs.py:682

        previous_human = next((message for message in reversed(messages[:target_index]) if _is_visible_human_message(message)), None)
        target_run_id = (
            await _find_target_run_id(
                thread_id,
                message_id,
                target_message,
                previous_human,
                request,
            )
            if previous_human is not None
            else None
        )
    if previous_human is None:
        raise HTTPException(status_code=409, detail="Could not find the user message for this assistant response")
    if target_run_id is None:
        raise HTTPException(status_code=409, detail="Could not find source run for assistant message")
    previous_human_id = _message_id(previous_human)
    if not previous_human_id:
        raise HTTPException(status_code=409, detail="The source user message is missing an id")

    base_checkpoint_tuple = await _find_base_checkpoint_before_human(
        thread_id,
        previous_human_id,
        request,
        head_checkpoint=latest_checkpoint,
    )
    checkpoint = _checkpoint_response(base_checkpoint_tuple)
    metadata = {
        "regenerate_from_message_id": message_id,
        "regenerate_from_run_id": target_run_id,
        "regenerate_checkpoint_id": checkpoint["checkpoint_id"],
    }
    regenerate_input: dict[str, Any] = {"messages": [_clean_human_message_for_regenerate(previous_human)]}
    latest_values = latest_checkpoint.values if isinstance(latest_checkpoint.values, dict) else {}
    latest_title = latest_values.get("title")
    if isinstance(latest_title, str) and latest_title:
        # Regenerate resumes from the checkpoint before the target human turn.

View on GitHub (pinned to 1dd6ba1acb)

Solutions

  1. Ensure every user message added to graph state has a stable id (LangChain message id or server-stamped).
  2. For legacy threads without ids, continue the conversation in a new thread rather than regenerating.
  3. Audit custom middleware/extensions that mutate checkpointed messages and preserve the id field.

Example fix

# before
state["messages"].append(HumanMessage(content=text))
# after
import uuid
state["messages"].append(HumanMessage(content=text, id=str(uuid.uuid4())))
Defensive patterns

Strategy: validation

Validate before calling

const idx = messages.findIndex(m => m.id === messageId);
const prevHuman = messages.slice(0, idx).findLast(m => m.type === 'human' && !m.hidden);
if (!prevHuman?.id) { disableRegenerate(); }

Type guard

function humanMessageHasId(m: {type: string; id?: string} | undefined): boolean {
  return !!m && m.type === 'human' && typeof m.id === 'string' && m.id.length > 0;
}

Try / catch

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; }

Prevention

When it happens

Trigger: 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.

Common situations: Threads created before an upgrade that started stamping message ids; custom code that appends HumanMessage(id=None); middleware that rewrites messages and loses ids.

Related errors


AI-assisted analysis of bytedance/deer-flow@1dd6ba1acb (2026-08-14). Data as JSON: /api/errors/430000a03d764e36. Report an issue: GitHub.