{"record":{"id":"58594df66d96f8fe","repo":"thedotmack/claude-mem","slug":"not-found","errorCode":"not_found","errorMessage":"not_found","messagePattern":"not_found","errorType":"http","errorClass":null,"httpStatus":404,"severity":"error","filePath":"src/server/routes/v1/ServerV1PostgresRoutes.ts","lineNumber":1074,"sourceCode":"      await server.connect(transport);\n      await transport.handleRequest(req, res, req.body);\n    });\n    // MCP streamable-HTTP only uses POST (JSON-RPC) and GET (SSE). Scope the\n    // route to those instead of app.all, so DELETE/PUT/PATCH/OPTIONS don't run\n    // auth + transport only to be rejected.\n    app.post('/v1/mcp', readAuth, mcpHandler);\n    app.get('/v1/mcp', readAuth, mcpHandler);\n\n    // DELETE /v1/memories/:id — forget a single observation (sources cascade).\n    app.delete('/v1/memories/:id', writeAuth, this.asyncHandler(async (req, res) => {\n      const teamId = this.requireTeamId(req, res);\n      if (!teamId) return;\n      const id = String(req.params.id);\n      const projectScope = req.authContext?.projectId ?? null;\n      try {\n        const deleted = await this.deleteObservationForScope(id, teamId, projectScope);\n        if (!deleted) {\n          res.status(404).json({ error: 'not_found' });\n          return;\n        }\n        await this.auditWrite(req, 'observation.deleted', id, projectScope, { via: 'api' });\n        res.status(200).json({ deleted: true, id });\n      } catch (error) {\n        const err = error instanceof Error ? error : new Error(String(error));\n        logger.warn('SYSTEM', 'observation.delete failed', { requestId: req.requestId ?? null }, err);\n        this.handleDbError(err, res, 'observation.delete');\n      }\n    }));\n\n    // DELETE /v1/projects/:projectId/memory — forget EVERYTHING captured for a\n    // project (observations, raw events, sessions, jobs). Keeps the project shell.\n    app.delete('/v1/projects/:projectId/memory', writeAuth, this.asyncHandler(async (req, res) => {\n      const teamId = this.requireTeamId(req, res);\n      if (!teamId) return;\n      const projectId = String(req.params.projectId);\n      if (!this.ensureProjectAllowed(req, res, projectId)) return;","sourceCodeStart":1056,"sourceCodeEnd":1092,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/e2d1df569a8f04075d40e92461128ece7cf04c82/src/server/routes/v1/ServerV1PostgresRoutes.ts#L1056-L1092","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Idempotent forget: 404 is an acceptable outcome for a delete\nconst res = await fetch(`${base}/v1/memories/${id}`, { method: 'DELETE', headers });\nif (res.status === 404) return { deleted: true, id, alreadyGone: true };\nif (!res.ok) throw new Error(`delete failed: ${res.status} ${await res.text()}`);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["http-404","memories","delete","idempotency","tenant-scoping"],"backgroundTag":"resource-not-found","analyzedSha":"e2d1df569a8f04075d40e92461128ece7cf04c82","analyzedAt":"2026-08-20T23:58:13.836Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}