bytedance/deer-flow · warning · HTTPException

Memory fact '{fact_id}' not found.

Error message

Memory fact '{fact_id}' not found.

What it means

404 raised when delete_fact raises KeyError: no fact with the given fact_id exists for the resolved user. Memory is per-user isolated (_resolve_memory_user_id), so a fact id that exists for another user is equally 'not found'.

Source

Thrown at backend/app/gateway/routers/memory.py:353

    return MemoryResponse(**memory_data)


@router.delete(
    "/memory/facts/{fact_id}",
    response_model=MemoryResponse,
    response_model_exclude_none=True,
    summary="Delete Memory Fact",
    description="Delete a single saved memory fact by its fact id.",
)
async def delete_memory_fact_endpoint(fact_id: str, http_request: Request) -> MemoryResponse:
    """Delete a single fact from memory by fact id."""
    manager = await asyncio.to_thread(get_memory_manager)
    try:
        memory_data = await asyncio.to_thread(manager.delete_fact, fact_id, user_id=_resolve_memory_user_id(http_request))
    except NotImplementedError:
        raise _unsupported_501(manager, "delete fact") from None
    except KeyError as exc:
        raise HTTPException(status_code=404, detail=f"Memory fact '{fact_id}' not found.") from exc
    except (MemoryConflictError, MemoryCorruptionError) as exc:
        raise _map_memory_manager_error(exc) from exc
    except OSError as exc:
        raise HTTPException(status_code=500, detail="Failed to delete memory fact.") from exc

    return MemoryResponse(**memory_data)


@router.patch(
    "/memory/facts/{fact_id}",
    response_model=MemoryResponse,
    response_model_exclude_none=True,
    summary="Patch Memory Fact",
    description="Partially update a single saved memory fact by its fact id while preserving omitted fields.",
)
async def update_memory_fact_endpoint(fact_id: str, request: FactPatchRequest, http_request: Request) -> MemoryResponse:
    """Partially update a single fact manually."""
    manager = await asyncio.to_thread(get_memory_manager)

View on GitHub (pinned to 1dd6ba1acb)

Solutions

  1. Re-fetch the current fact list (GET /api/memory) and delete using a fresh id
  2. Treat 404 on delete as success if the goal is the fact being gone (idempotent delete handling)
  3. Confirm the request carries the same user identity that created the fact

Example fix

// before
await fetch(`/api/memory/facts/${id}`, {method:'DELETE'}).then(r => { if (!r.ok) throw new Error('delete failed'); });
// after
const res = await fetch(`/api/memory/facts/${id}`, {method:'DELETE'});
if (res.status === 404) { console.info('fact already removed'); return; }
if (!res.ok) throw new Error('delete failed');
Defensive patterns

Strategy: try-catch

Validate before calling

const mem = await getMemory(); if (!mem.facts?.[factId]) { console.info('fact already absent'); return; } // proceed to DELETE

Type guard

function factExists(factId: string, memory: {facts?: Record<string, unknown>}): boolean { return Boolean(memory.facts && factId in memory.facts); }

Try / catch

try { await deleteFact(id); } catch (e) { if (e.status === 404) return; /* already gone — treat as success */ throw e; }

Prevention

When it happens

Trigger: DELETE /api/memory/facts/{fact_id} with an id from an outdated UI list; deleting a fact already removed by a concurrent request or by the agent itself; sending a fact id belonging to a different user; malformed id strings that never match stored keys.

Common situations: Stale frontend lists after background memory maintenance; double-click delete firing two requests; id copy errors (truncated UUIDs); switching authenticated users while reusing cached fact ids.

Related errors


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