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 False

View on GitHub (pinned to 1dd6ba1acb)

Solutions

  1. Wait for the current run to reach a terminal state (success) before invoking edit-regenerate.
  2. Check the run status via the runs endpoint and only enable the Edit affordance for completed turns with text content.
  3. If the last run failed, regenerate it first so a completed assistant text message exists, then edit.
  4. 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

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


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