HKUDS/DeepTutor · warning · HTTPException

{RunBusyError message}

Error message

{RunBusyError message}

What it means

HTTP 409 from POST /memory/runs/{run_id}/undo when undo_last raises RunBusyError — the run (or the memory subsystem it targets) is currently active/busy and cannot accept an undo while work is in flight.

Source

Thrown at deeptutor/api/routers/memory.py:386

    if not ok:
        raise HTTPException(status_code=409, detail="not active")
    return {"run_id": run_id, "cancelled": True}


@router.post("/runs/{run_id}/undo")
async def undo_run_edit(run_id: str):
    from deeptutor.services.memory.consolidator.runs import (
        RunBusyError,
        get_run_manager,
    )

    manager = get_run_manager()
    try:
        event = await manager.undo_last(run_id)
    except KeyError:
        raise HTTPException(status_code=404, detail="unknown run_id")
    except RunBusyError as exc:
        raise HTTPException(status_code=409, detail=str(exc))
    if event is None:
        raise HTTPException(status_code=409, detail="nothing to undo")
    run = manager.get(run_id)
    return {
        "run_id": run_id,
        "undone": True,
        "undo_count": len(run.undo_stack) if run else 0,
        "event": {"seq": event.seq, "ts": event.ts, **event.payload},
    }


@router.get("/runs")
async def list_runs(layer: str | None = None, key: str | None = None):
    from deeptutor.services.memory.consolidator.runs import get_run_manager

    lyr = _validate_layer(layer) if layer is not None else None
    if lyr and key is not None:
        _validate_doc_key(lyr, key)

View on GitHub (pinned to 3e82f13042)

Solutions

  1. Wait for the run to reach a completed state (poll GET /runs/{run_id}) before calling undo
  2. Disable the undo action in the UI while run state is active
  3. Serialize exclusive run operations (undo/cancel/start) behind a single client-side queue

Example fix

// before
await fetch(`/memory/runs/${id}/undo`, {method:'POST'});
// after
const run = await (await fetch(`/memory/runs/${id}`)).json();
if (run.state === 'completed') {
  await fetch(`/memory/runs/${id}/undo`, {method:'POST'});
}
Defensive patterns

Strategy: retry

Validate before calling

const run = await getRun(id);
if (run.state !== 'completed') await waitTerminal(id);

Type guard

function isTerminal(state: string): boolean { return ['completed','failed','cancelled'].includes(state); }

Try / catch

try { await undoRun(id); } catch (e) { if (e.status === 409) { await waitTerminal(id); await undoRun(id); } else throw e; }

Prevention

When it happens

Trigger: Calling undo while the consolidation run is still executing, or while another exclusive operation (another undo, a cancel, a new run) holds the run manager's lock.

Common situations: Users clicking 'undo' as soon as results stream in before the run reaches a terminal state, or concurrent undo + cancel requests racing each other.

Related errors


AI-assisted analysis of HKUDS/DeepTutor@3e82f13042 (2026-08-27). Data as JSON: /api/errors/057f58956c09994b. Report an issue: GitHub.