{"record":{"id":"cf0651321ad3a128","repo":"bytedance/deer-flow","slug":"only-successful-assistant-runs-can-be-edited-and-r","errorCode":null,"errorMessage":"Only successful assistant runs can be edited and rerun","messagePattern":"Only successful assistant runs can be edited and rerun","errorType":"http","errorClass":"HTTPException","httpStatus":409,"severity":"warning","filePath":"backend/app/gateway/routers/thread_runs.py","lineNumber":600,"sourceCode":"    return str(status) if status is not None else None\n\n\nasync def _require_successful_source_run(thread_id: str, run_id: str, request: Request) -> RunRecord:\n    run_mgr = get_run_manager(request)\n    user_id = await get_current_user(request)\n    record = await run_mgr.get(run_id, user_id=user_id)\n    if record is None:\n        # The run-event journal is the authoritative lookup above. This fallback\n        # only covers recent in-memory/store hydration gaps for the latest turn.\n        records = await run_mgr.list_by_thread(thread_id, user_id=user_id, limit=20)\n        record = next((candidate for candidate in records if getattr(candidate, \"run_id\", None) == run_id), None)\n    if record is None:\n        raise HTTPException(status_code=409, detail=\"Could not find source run for assistant message\")\n    record_thread_id = getattr(record, \"thread_id\", None)\n    if isinstance(record_thread_id, str) and record_thread_id and record_thread_id != thread_id:\n        raise HTTPException(status_code=409, detail=\"Could not find source run for assistant message\")\n    if _run_status_value(record) != RunStatus.success.value:\n        raise HTTPException(status_code=409, detail=\"Only successful assistant runs can be edited and rerun\")\n    return record\n\n\nasync def _find_interrupted_target_run_id(\n    thread_id: str,\n    source_human: Any,\n    request: Request,\n) -> str | None:\n    source_run_id = _message_additional_kwargs(source_human).get(\"run_id\")\n    if not isinstance(source_run_id, str) or not source_run_id:\n        return None\n\n    run_mgr = get_run_manager(request)\n    user_id = await get_current_user(request)\n    record = await run_mgr.get(source_run_id, user_id=user_id)\n    if record is None:\n        records = await run_mgr.list_by_thread(thread_id, user_id=user_id, limit=20)\n        record = next(","sourceCodeStart":582,"sourceCodeEnd":618,"githubUrl":"https://github.com/bytedance/deer-flow/blob/1dd6ba1acb03700589994b0366c5d1c7d05e2eff/backend/app/gateway/routers/thread_runs.py#L582-L618","documentation":"Raised in _require_successful_source_run (HTTP 409) when the located run record's status is not RunStatus.success. Edit-and-rerun only replays runs that completed cleanly; interrupted, error, or in-flight runs are refused because their checkpointed state may be partial.","triggerScenarios":"Rerunning a run whose status is 'error' (model failure), 'interrupted' (human-in-the-loop pause or cancel), or still running.","commonSituations":"User retries a failed run via the edit endpoint instead of a normal retry; editing a turn whose previous run was cancelled; racing a still-streaming run.","solutions":["Check the run's status via the runs list endpoint and only rerun runs with status 'success'.","For failed runs, use the normal run/retry path (send a new message) rather than edit-rerun.","Wait for in-flight runs to finish before initiating edit.","If a run is stuck in a non-terminal status, cancel/cleanup it first via the runs lifecycle endpoint."],"exampleFix":"// before\nawait editRerun(threadId, runId, newText);\n// after\nconst run = await getRun(threadId, runId);\nif (run.status !== 'success') throw new Error(`Run is ${run.status}; only successful runs can be edited`);\nawait editRerun(threadId, runId, newText);","handlingStrategy":"validation","validationCode":"const run = await getRun(threadId, runId);\nif (run.status !== 'success') { disableEditAffordance(); return; }","typeGuard":"function isSuccessfulRun(run: {status: string}): boolean {\n  return run.status === 'success';\n}","tryCatchPattern":"try { await editRerun(threadId, runId, text); } catch (e) { if (e.status === 409 && /successful assistant runs/.test(e.detail)) { notify('This turn did not complete successfully; send a new message instead'); } else throw e; }","preventionTips":["Only enable Edit on turns whose run succeeded","Handle failed runs via retry/send, not edit-rerun"],"tags":["run-status","regenerate","http-409"],"backgroundTag":null,"analyzedSha":"1dd6ba1acb03700589994b0366c5d1c7d05e2eff","analyzedAt":"2026-08-14T21:20:34.804Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}