{"record":{"id":"66cb2af93a9fbb5f","repo":"thedotmack/claude-mem","slug":"agent-event-id-must-belong-to-project-id-and-team","errorCode":null,"errorMessage":"agent_event_id must belong to project_id and team_id","messagePattern":"agent_event_id must belong to project_id and team_id","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/storage/postgres/observations.ts","lineNumber":372,"sourceCode":"  }\n  if (input.sourceType === 'agent_event' && row.agent_event_id !== input.agentEventId) {\n    throw new Error('generation_job_id agent_event_id must match observation source');\n  }\n}\n\nasync function assertAgentEventOwnership(\n  client: PostgresQueryable,\n  agentEventId: string,\n  projectId: string,\n  teamId: string\n): Promise<void> {\n  const row = await queryOne<{ id: string }>(\n    client,\n    'SELECT id FROM agent_events WHERE id = $1 AND project_id = $2 AND team_id = $3',\n    [agentEventId, projectId, teamId]\n  );\n  if (!row) {\n    throw new Error('agent_event_id must belong to project_id and team_id');\n  }\n}\n\nasync function assertObservationOwnership(\n  client: PostgresQueryable,\n  observationId: string,\n  projectId: string,\n  teamId: string\n): Promise<void> {\n  const row = await queryOne<{ id: string }>(\n    client,\n    'SELECT id FROM observations WHERE id = $1 AND project_id = $2 AND team_id = $3',\n    [observationId, projectId, teamId]\n  );\n  if (!row) {\n    throw new Error('observation_reindex source_id must belong to project_id and team_id');\n  }\n}","sourceCodeStart":354,"sourceCodeEnd":390,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/d8bc9755e74915e5c3b999181e10a67c889bce2a/src/storage/postgres/observations.ts#L354-L390","documentation":"For 'agent_event' sources, assertAgentEventOwnership checks that the agent event id exists in agent_events scoped to project_id and team_id. If not found, the event is unknown, belongs to another tenant, or was deleted, so the source cannot be attached.","triggerScenarios":"addSource with sourceType='agent_event' where input.sourceId (the agent event id) is not a row in agent_events for the supplied projectId/teamId.","commonSituations":"Ingesting events from another environment; tenant id mixups; the event was purged before observations were generated; agent event ids fabricated in a client instead of returned by the ingestion API.","solutions":["Verify the event exists: SELECT id FROM agent_events WHERE id = $1 AND project_id = $2 AND team_id = $3.","Ingest/persist the agent event first, then attach the observation source using the returned id.","Check that projectId/teamId match the tenant under which the event was stored."],"exampleFix":"// before\nawait repo.addSource({ sourceType: 'agent_event', sourceId: uningestedEventId, ... });\n// after\nconst evt = await agentEvents.ingest(event); // returns id in same project/team\nawait repo.addSource({ sourceType: 'agent_event', sourceId: evt.id, ... });","handlingStrategy":"validation","validationCode":"const evt = await db.oneOrNone(\n  'SELECT 1 FROM agent_events WHERE id = $1 AND project_id = $2 AND team_id = $3',\n  [eventId, projectId, teamId]\n);\nif (!evt) throw new Error(`agent event ${eventId} not ingested in scope`);","typeGuard":null,"tryCatchPattern":"try {\n  await repo.addSource(input);\n} catch (e) {\n  if (e instanceof Error && e.message === 'agent_event_id must belong to project_id and team_id') {\n    logger.warn('skip source: agent event not found', { eventId: input.sourceId });\n    return null;\n  }\n  throw e;\n}","preventionTips":["Always obtain agent event ids from the ingestion API response, never construct them.","Ingest the event and attach its observation source within one transaction.","Verify tenant ids of events and observations come from the same context object."],"tags":["postgres","agent-events","tenant-scope"],"backgroundTag":"record-not-found","analyzedSha":"d8bc9755e74915e5c3b999181e10a67c889bce2a","analyzedAt":"2026-09-17T16:40:26.182Z","contentChangedAt":"2026-09-17T16:40:26.182Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}