bytedance/deer-flow · warning · HTTPException
Only completed assistant text turns can be edited
Error message
Only completed assistant text turns can be edited
What it means
Raised by _latest_editable_turn (HTTP 409) when, after the target human message, there is no visible AI message that is a terminal assistant text message. Editing requires a completed assistant text turn to anchor the replay; tool-call stubs, interrupted responses, or non-text outputs do not qualify.
Source
Thrown at backend/app/gateway/routers/thread_runs.py:453
goal = _checkpoint_values(snapshot).get("goal")
return isinstance(goal, dict) and goal.get("status") == "active"
def _latest_editable_turn(messages: list[Any], human_message_id: str) -> tuple[int, Any, int, Any, list[str]]:
latest_human_index = next((index for index in range(len(messages) - 1, -1, -1) if _is_visible_human_message(messages[index])), None)
if latest_human_index is None or _message_id(messages[latest_human_index]) != human_message_id:
raise HTTPException(status_code=409, detail="Only the latest completed user turn can be edited")
source_human = messages[latest_human_index]
last_ai_index: int | None = None
for index, message in enumerate(messages[latest_human_index + 1 :], start=latest_human_index + 1):
if _is_visible_human_message(message):
break
if _is_visible_ai_message(message):
last_ai_index = index
if last_ai_index is None or not _is_terminal_assistant_text_message(messages[last_ai_index]):
raise HTTPException(status_code=409, detail="Only completed assistant text turns can be edited")
source_message_ids = [message_id for message in messages[latest_human_index : last_ai_index + 1] if (message_id := _message_id(message))]
return latest_human_index, source_human, last_ai_index, messages[last_ai_index], source_message_ids
def _event_message_id(row: dict[str, Any]) -> str | None:
content = row.get("content")
if isinstance(content, BaseMessage):
return _message_id(content)
if isinstance(content, dict):
return _message_id(content)
return None
def _run_last_ai_matches_message(record: RunRecord, message: Any) -> bool:
last_ai_message = (record.last_ai_message or "").strip()
if not last_ai_message:
return FalseView on GitHub (pinned to 1dd6ba1acb)
Solutions
- Wait for the current run to reach a terminal state (success) before invoking edit-regenerate.
- Check the run status via the runs endpoint and only enable the Edit affordance for completed turns with text content.
- If the last run failed, regenerate it first so a completed assistant text message exists, then edit.
- Verify _is_terminal_assistant_text_message criteria are met (plain text content, no pending tool_calls) for the target turn.
Example fix
// before await editPrepare(threadId, humanId, newText); // fired right after send // after await waitForRunCompletion(threadId, runId); const state = await getThreadState(threadId); const lastAi = [...state.messages].reverse().find(m => m.type === 'ai' && !m.tool_calls); if (lastAi) await editPrepare(threadId, humanId, newText);
Defensive patterns
Strategy: validation
Validate before calling
const run = await getLatestRun(threadId); if (run.status !== 'success') return; // no completed assistant turn to edit yet
Type guard
function isCompletedAssistantTurn(msg: Msg | undefined): msg is Msg {
return !!msg && msg.type === 'ai' && !msg.hidden && !msg.tool_calls && typeof msg.content === 'string' && msg.content.length > 0;
} Try / catch
try { await editPrepare(threadId, humanId, text); } catch (e) { if (e.status === 409 && /completed assistant text/.test(e.detail)) { notify('Wait for the response to finish before editing'); } else throw e; } Prevention
- Gate Edit behind run completion
- Treat interrupted runs as non-editable
- Re-check terminal AI message presence after streaming ends
When it happens
Trigger: Editing a turn whose assistant response is still streaming or was interrupted mid-LLM-call; the turn ended with only tool-call/AI messages that are not terminal text; the last AI message was filtered out as hidden.
Common situations: User clicks Edit while a run is still in flight; the previous run errored before emitting a final text message; assistant response consisted only of artifacts/tool output.
Related errors
- Only the latest completed user turn can be edited
- Checkpoint is missing checkpoint_id
- Could not find source run for assistant message
- Could not safely resolve the checkpoint before the target us
- Could not find an addressable checkpoint before the target u
AI-assisted analysis of bytedance/deer-flow@1dd6ba1acb (2026-08-14).
Data as JSON: /api/errors/5a6bf8f40b6072ce.
Report an issue: GitHub.