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

  1. Treat 404 on delete as success — the observation is gone from your scope either way (idempotent forget)
  2. Confirm the id with GET /v1/memories/:id before deleting when you need to distinguish causes
  3. 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

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.