{"record":{"id":"128af556e0c2ca9d","repo":"bytedance/deer-flow","slug":"failed-to-delete-agent-str-e","errorCode":null,"errorMessage":"Failed to delete agent: {str(e)}","messagePattern":"Failed to delete agent: (.+?)","errorType":"http","errorClass":"HTTPException","httpStatus":500,"severity":"error","filePath":"backend/app/gateway/routers/agents.py","lineNumber":559,"sourceCode":"    Args:\n        name: The agent name.\n\n    Raises:\n        HTTPException: 404 if no per-user copy exists; 409 if only a legacy\n            shared copy exists (suggesting the migration script).\n    \"\"\"\n    _require_agents_api_enabled()\n    _validate_agent_name(name)\n    name = _normalize_agent_name(name)\n    user_id = get_effective_user_id()\n    store = get_agent_store()\n\n    try:\n        # Off the event loop: file rmtree or a DB delete plus memory cleanup.\n        outcome = await asyncio.to_thread(store.delete, name, user_id=user_id)\n    except Exception as e:\n        logger.error(f\"Failed to delete agent '{name}': {e}\", exc_info=True)\n        raise HTTPException(status_code=500, detail=f\"Failed to delete agent: {str(e)}\")\n\n    if outcome == \"legacy\":\n        raise HTTPException(\n            status_code=409,\n            detail=(f\"Agent '{name}' only exists in the legacy shared layout and is not scoped to a user. Run scripts/migrate_user_isolation.py to move legacy agents into the per-user layout before deleting.\"),\n        )\n    if outcome == \"missing\":\n        raise HTTPException(status_code=404, detail=f\"Agent '{name}' not found\")\n    if outcome == \"not-custom-agent\":\n        raise HTTPException(\n            status_code=409,\n            detail=(f\"Directory for '{name}' contains memory data but is not a custom agent because config.yaml is missing; it was preserved.\"),\n        )\n\n    logger.info(f\"Deleted agent '{name}'\")\n","sourceCodeStart":541,"sourceCodeEnd":575,"githubUrl":"https://github.com/bytedance/deer-flow/blob/1dd6ba1acb03700589994b0366c5d1c7d05e2eff/backend/app/gateway/routers/agents.py#L541-L575","documentation":"Catch-all 500 on DELETE `/agents/{name}`: `store.delete` itself raised — rmtree permission errors, DB delete failures — before any outcome classification. Note the distinction: outcome values 'legacy'/'missing'/'not-custom-agent' map to 409/404/409 respectively; this 500 means the delete operation threw rather than returning an outcome.","triggerScenarios":"Files under the agent dir owned by root so rmtree fails midway; DB connection dropped mid-delete; memory cleanup raising after config removal.","commonSituations":"Agents created by a different uid (earlier container run as root); NFS volume refusing recursive deletes; partial delete leaving the agent in a broken state.","solutions":["Read the traceback logged with `Failed to delete agent '<name>'`","Fix ownership of the agent directory tree so the Gateway user can remove it, then retry","If a partial delete occurred, verify remaining files and clean up manually before re-creating","For DB backends, check DB health"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { await api.deleteAgent(name); }\ncatch (e) {\n  if (e.status === 500 && /Failed to delete/.test(e.detail)) {\n    const still = await api.listAgents(); // delete may have partially completed\n    if (still.agents.some((a) => a.name === name)) throw new AgentDeleteFailedError(name);\n    return; // actually gone despite the error\n  }\n  throw e;\n}","preventionTips":["After a failed delete, verify with a fresh list — partial deletes happen","Keep agent directory ownership aligned with the Gateway process user","Alert on delete 500s; rmtree permission errors are the usual cause"],"tags":["http-500","agents","delete","permissions"],"backgroundTag":null,"analyzedSha":"1dd6ba1acb03700589994b0366c5d1c7d05e2eff","analyzedAt":"2026-08-14T21:20:34.804Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}