{"record":{"id":"ca3b1e9dc4b55c05","repo":"thedotmack/claude-mem","slug":"observation-reindex-source-id-must-belong-to-proje-ca3b1e","errorCode":null,"errorMessage":"observation_reindex source_id must belong to project_id and team_id","messagePattern":"observation_reindex source_id must belong to project_id and team_id","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/storage/postgres/observations.ts","lineNumber":388,"sourceCode":"  );\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}\n\nfunction mapObservationRow(row: ObservationRow): PostgresObservation {\n  return {\n    id: row.id,\n    projectId: row.project_id,\n    teamId: row.team_id,\n    serverSessionId: row.server_session_id,\n    kind: row.kind,\n    content: row.content,\n    generationKey: row.generation_key,\n    metadata: toJsonObject(row.metadata),\n    embedding: row.embedding,\n    createdByJobId: row.created_by_job_id,\n    createdAtEpoch: toEpoch(row.created_at),\n    updatedAtEpoch: toEpoch(row.updated_at)\n  };","sourceCodeStart":370,"sourceCodeEnd":406,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/d768ba364302d12b76e69e4f021f0bb1d2d50ed6/src/storage/postgres/observations.ts#L370-L406","documentation":"Thrown by assertObservationOwnership (private helper) when addSource is called with sourceType 'observation_reindex' and no generationJobId. It looks up observations by (id=sourceId, project_id, team_id); missing means the source observation is not in scope. The message reuses the 'observation_reindex source_id' wording to identify which field is wrong.","triggerScenarios":"Calling addSource({sourceType:'observation_reindex', sourceId}) without a generationJobId, where sourceId is not an observation in the given project/team. (When a generationJobId is present, this branch is skipped in favor of assertGenerationJobMatchesSource.)","commonSituations":"Reindexing an observation from the wrong tenant; deleted source observation; passing an observation_source id instead of the observation id.","solutions":["Resolve the observation via getByIdForScope before adding it as a reindex source.","Confirm sourceId is the observation id (not a source row id or job id).","If the source was deleted, pick a current observation or recreate it."],"exampleFix":"// before\nawait sources.addSource({ observationId, projectId, teamId, sourceType:'observation_reindex', sourceId: maybeSourceRowId });\n// after\nconst src = await observations.getByIdForScope({ id: sourceId, projectId, teamId });\nif (!src) throw new Error('source observation not in scope');\nawait sources.addSource({ observationId, projectId, teamId, sourceType:'observation_reindex', sourceId });","handlingStrategy":"validation","validationCode":"const src = await observations.getByIdForScope({ id: sourceId, projectId, teamId });\nif (!src) throw new Error('source observation not in scope');","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use the observation id, not the source-row id, when reindexing."],"tags":["postgres","validation","multi-tenant","ownership","observations"],"backgroundTag":null,"analyzedSha":"d768ba364302d12b76e69e4f021f0bb1d2d50ed6","analyzedAt":"2026-08-12T23:52:55.241Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}