thedotmack/claude-mem · error
not_found
not_found
Error message
not_found
What it means
DELETE /v1/memories/:id calls deleteObservationForScope, which matches by id plus the caller's team (and the key's project scope when set). When zero rows match it answers 404 {error:'not_found'}. The 404 deliberately does not distinguish 'never existed' from 'owned by another team/project' to avoid leaking existence.
Source
Thrown at src/server/routes/v1/ServerV1PostgresRoutes.ts:1074
await server.connect(transport);
await transport.handleRequest(req, res, req.body);
});
// MCP streamable-HTTP only uses POST (JSON-RPC) and GET (SSE). Scope the
// route to those instead of app.all, so DELETE/PUT/PATCH/OPTIONS don't run
// auth + transport only to be rejected.
app.post('/v1/mcp', readAuth, mcpHandler);
app.get('/v1/mcp', readAuth, mcpHandler);
// DELETE /v1/memories/:id — forget a single observation (sources cascade).
app.delete('/v1/memories/:id', writeAuth, this.asyncHandler(async (req, res) => {
const teamId = this.requireTeamId(req, res);
if (!teamId) return;
const id = String(req.params.id);
const projectScope = req.authContext?.projectId ?? null;
try {
const deleted = await this.deleteObservationForScope(id, teamId, projectScope);
if (!deleted) {
res.status(404).json({ error: 'not_found' });
return;
}
await this.auditWrite(req, 'observation.deleted', id, projectScope, { via: 'api' });
res.status(200).json({ deleted: true, id });
} catch (error) {
const err = error instanceof Error ? error : new Error(String(error));
logger.warn('SYSTEM', 'observation.delete failed', { requestId: req.requestId ?? null }, err);
this.handleDbError(err, res, 'observation.delete');
}
}));
// DELETE /v1/projects/:projectId/memory — forget EVERYTHING captured for a
// project (observations, raw events, sessions, jobs). Keeps the project shell.
app.delete('/v1/projects/:projectId/memory', writeAuth, this.asyncHandler(async (req, res) => {
const teamId = this.requireTeamId(req, res);
if (!teamId) return;
const projectId = String(req.params.projectId);
if (!this.ensureProjectAllowed(req, res, projectId)) return;View on GitHub (pinned to e2d1df569a)
Solutions
- Treat 404 on delete as success — the observation is gone from your scope either way (idempotent forget)
- Confirm the id with GET /v1/memories/:id before deleting when you need to distinguish causes
- If using a project-scoped key, verify which project owns the observation before deleting
Defensive patterns
Strategy: validation
Validate before calling
// Idempotent forget: 404 is an acceptable outcome for a delete
const res = await fetch(`${base}/v1/memories/${id}`, { method: 'DELETE', headers });
if (res.status === 404) return { deleted: true, id, alreadyGone: true };
if (!res.ok) throw new Error(`delete failed: ${res.status} ${await res.text()}`); Prevention
- Design delete flows to treat 404 as success — the observation is out of scope either way
- When the cause matters, GET the observation first and handle 404 there
- Deduplicate delete triggers (UI + script) so the second call never fires
When it happens
Trigger: DELETE /v1/memories/<id> where the id is wrong, already deleted, owned by another team, or — under a project-scoped key — belongs to a sibling project within the same team.
Common situations: Double-invoked delete handlers where the second call 404s; observation deleted from a dashboard while a script deletes it again via API; project-scoped key used against team-wide observation ids.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
AI-assisted analysis of thedotmack/claude-mem@e2d1df569a (2026-08-20).
Data as JSON: /api/errors/58594df66d96f8fe.
Report an issue: GitHub.