{"record":{"id":"5f5555d2856e7e4f","repo":"thedotmack/claude-mem","slug":"notfound","errorCode":"NotFound","errorMessage":"Event not found","messagePattern":"Event not found","errorType":"http","errorClass":null,"httpStatus":404,"severity":"warning","filePath":"src/server/routes/v1/ServerV1PostgresRoutes.ts","lineNumber":452,"sourceCode":"      res.json({ event: serializeEvent(fullEvent) });\n    }));\n\n    // GET /v1/events/:id/observations — list observations linked to event via observation_sources.\n    // Scope is enforced by joining observations.team_id = $teamId and the\n    // event ownership check before any rows are returned. Cross-tenant\n    // requests are reported as 404 to avoid revealing existence.\n    app.get('/v1/events/:id/observations', readAuth, this.asyncHandler(async (req, res) => {\n      const teamId = this.requireTeamId(req, res);\n      if (!teamId) return;\n      const id = this.routeParam(req.params.id);\n\n      const eventResult = await this.options.pool.query(\n        `SELECT id, project_id FROM agent_events WHERE id = $1 AND team_id = $2`,\n        [id, teamId],\n      );\n      const eventRow = eventResult.rows[0] as undefined | { id: string; project_id: string };\n      if (!eventRow) {\n        res.status(404).json({ error: 'NotFound', message: 'Event not found' });\n        return;\n      }\n      if (!this.ensureProjectAllowed(req, res, eventRow.project_id)) return;\n\n      const obsResult = await this.options.pool.query(\n        `\n          SELECT o.id, o.project_id, o.team_id, o.server_session_id, o.kind, o.content,\n                 o.metadata, o.generation_key, o.created_by_job_id, o.created_at, o.updated_at,\n                 os.id AS source_id_pk, os.source_type, os.source_id, os.generation_job_id, os.created_at AS source_created_at\n          FROM observation_sources os\n          INNER JOIN observations o ON o.id = os.observation_id\n          WHERE os.source_type = 'agent_event'\n            AND os.source_id = $1\n            AND o.team_id = $2\n            AND o.project_id = $3\n          ORDER BY o.created_at ASC\n        `,\n        [eventRow.id, teamId, eventRow.project_id],","sourceCodeStart":434,"sourceCodeEnd":470,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/e2d1df569a8f04075d40e92461128ece7cf04c82/src/server/routes/v1/ServerV1PostgresRoutes.ts#L434-L470","documentation":"404 from GET /v1/events/:id/observations when no agent_events row matches the id for the caller's team. The lookup is team-scoped (WHERE id = $1 AND team_id = $2), so an event that exists under another team is indistinguishable from one that never existed — deliberate, to avoid revealing existence. A nonexistent id and a cross-tenant id return the same response.","triggerScenarios":"GET /v1/events/<wrong-or-typo'd-id>/observations; event id from a different team/environment; event not yet ingested when observations are polled right after creation; key's team differs from the event's team.","commonSituations":"Polling for observations before the create call completed; copying ids between staging and prod; truncating UUIDs during manual testing.","solutions":["Confirm the event create call returned 201 and use the returned event id verbatim.","Verify the id belongs to the caller's team (events listing scoped to the key's team).","Treat 404 as terminal for that id — retrying without fixing the id or scope cannot succeed.","Wait for ingestion before first poll if the event was just created."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Use the id from the create response, never a hand-copied one\nconst created = await postJson('/v1/events', eventPayload);\nconst eventId: string = created.event.id;\nconst observations = await getJson(`/v1/events/${encodeURIComponent(eventId)}/observations`);","typeGuard":"interface NotFoundBody { error: string; message: string }\nfunction isEventNotFound(res: Response, body: unknown): boolean {\n  return res.status === 404 && typeof body === 'object' && body !== null &&\n    (body as NotFoundBody).error === 'NotFound' && (body as NotFoundBody).message === 'Event not found';\n}","tryCatchPattern":null,"preventionTips":["Propagate server-returned ids end-to-end instead of reconstructing them.","Poll observations only after the create call succeeded.","Treat 404 as terminal for that id; never blind-retry."],"tags":["http-404","observations","events","tenancy"],"backgroundTag":"api-resource-not-found","analyzedSha":"e2d1df569a8f04075d40e92461128ece7cf04c82","analyzedAt":"2026-08-20T23:58:13.836Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}