{"record":{"id":"e0092278dd7763c6","repo":"thedotmack/claude-mem","slug":"manual-observation-sources-cannot-be-linked-to-a-g","errorCode":null,"errorMessage":"manual observation sources cannot be linked to a generation_job_id","messagePattern":"manual observation sources cannot be linked to a generation_job_id","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/storage/postgres/observations.ts","lineNumber":332,"sourceCode":"  );\n  if (!row) {\n    throw new Error('generation_job_id must belong to project_id and team_id');\n  }\n}\n\nasync function assertGenerationJobMatchesSource(\n  client: PostgresQueryable,\n  input: {\n    generationJobId: string;\n    projectId: string;\n    teamId: string;\n    sourceType: ObservationSourceType;\n    sourceId: string;\n    agentEventId: string | null;\n  }\n): Promise<void> {\n  if (input.sourceType === 'manual') {\n    throw new Error('manual observation sources cannot be linked to a generation_job_id');\n  }\n\n  const row = await queryOne<{\n    id: string;\n    source_type: string;\n    source_id: string;\n    agent_event_id: string | null;\n  }>(\n    client,\n    `\n      SELECT id, source_type, source_id, agent_event_id\n      FROM observation_generation_jobs\n      WHERE id = $1 AND project_id = $2 AND team_id = $3\n    `,\n    [input.generationJobId, input.projectId, input.teamId]\n  );\n  if (!row) {\n    throw new Error('generation_job_id must belong to project_id and team_id');","sourceCodeStart":314,"sourceCodeEnd":350,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/d768ba364302d12b76e69e4f021f0bb1d2d50ed6/src/storage/postgres/observations.ts#L314-L350","documentation":"Thrown by assertGenerationJobMatchesSource when addSource is called with sourceType 'manual' and a non-null generationJobId. Manual sources are user-authored and by design cannot be attributed to an automated generation job, so linking them is rejected before any DB lookup.","triggerScenarios":"Calling addSource({sourceType:'manual', generationJobId: '...'}). Any non-null generationJobId with a manual source triggers this immediately.","commonSituations":"UI defaulting generationJobId from context for all source types; importing manual notes while passing a job id; refactor that forgot to clear generationJobId for manual entries.","solutions":["For manual sources, omit generationJobId (or pass null/undefined).","Split the flow: manual sources never carry a job id; only agent_event/session_summary/observation_reindex may."],"exampleFix":"// before\nawait sources.addSource({ observationId, projectId, teamId, sourceType:'manual', sourceId: userId, generationJobId: jobId });\n// after\nawait sources.addSource({ observationId, projectId, teamId, sourceType:'manual', sourceId: userId, generationJobId: null });","handlingStrategy":"validation","validationCode":"if (sourceType==='manual' && generationJobId != null) { throw new TypeError('manual sources cannot carry a generationJobId'); }","typeGuard":"function isManualSourceUnlinked(input){ return input.sourceType!=='manual' || input.generationJobId == null; }","tryCatchPattern":null,"preventionTips":["Clear generationJobId whenever sourceType is manual."],"tags":["postgres","validation","observations","invariant"],"backgroundTag":null,"analyzedSha":"d768ba364302d12b76e69e4f021f0bb1d2d50ed6","analyzedAt":"2026-08-12T23:52:55.241Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}