thedotmack/claude-mem · error · Error
observation_reindex source_id must belong to project_id and…
Error message
observation_reindex source_id must belong to project_id and team_id
What it means
For sourceType 'observation_reindex', validateSource requires an observation row to exist with the given id under the job's project_id and team_id; otherwise the create is rejected. This ensures reindex jobs only target observations in the caller's scope.
Solutions
- Verify the observation exists with a scoped SELECT (id, project_id, team_id) before creating the job
- Use the correct observation id (not a memory or event id) from the same project/team
- If the observation was deleted, restore it or skip the reindex job
- Check tenant context: projectId/teamId must be the ones the observation was created under
Example fix
// before
await jobs.create({ sourceType: 'observation_reindex', sourceId: obsId, projectId, teamId });
// after
const obs = await client.query('SELECT 1 FROM observations WHERE id=$1 AND project_id=$2 AND team_id=$3', [obsId, projectId, teamId]);
if (obs.rowCount === 0) throw new Error('observation not in scope');
await jobs.create({ sourceType: 'observation_reindex', sourceId: obsId, projectId, teamId }); Defensive patterns
Strategy: validation
Validate before calling
const obs = await client.query('SELECT 1 FROM observations WHERE id=$1 AND project_id=$2 AND team_id=$3', [sourceId, projectId, teamId]);
if (obs.rowCount === 0) throw new Error('observation not found in scope'); Try / catch
try {
await jobs.create(input);
} catch (err) {
if (err instanceof Error && err.message.includes('observation_reindex source_id must belong')) {
throw new ScopeError('Observation missing or out of tenant scope');
}
throw err;
} Prevention
- Verify observation existence with scoped queries before creating reindex jobs
- Skip reindex jobs whose source was deleted; log instead of throwing
- Keep observation ids in the same tenant context as the job
When it happens
Trigger: create() with sourceType 'observation_reindex' where no observation matches the sourceId/projectId/teamId combination (id does not exist, or belongs to another project/team).
Common situations: Reindexing after a data deletion removed the observation; referencing an observation id from another tenant; using a memory/summary id instead of an observation id; id passed with different casing/format.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- agent_event source_id must belong to project_id and team_id
- generation_job_id must belong to project_id and team_id
- server_session_id must match the agent_event…
- session_summary source_id must equal server_session_id
- Adapter rejected input
AI-assisted analysis of thedotmack/claude-mem@d8bc9755e7 (2026-09-17).
Data as JSON: /api/errors/4528765b56feaf0b.
Report an issue: GitHub.
Appendix: source
Thrown at src/storage/postgres/generation-jobs.ts:296
return;
}
if (input.sourceType === 'session_summary') {
const sessionId = input.serverSessionId ?? input.sourceId;
await assertSessionOwnership(this.client, sessionId, input.projectId, input.teamId);
if (input.sourceId !== sessionId) {
throw new Error('session_summary source_id must equal server_session_id');
}
return;
}
const observation = await queryOne<{ id: string }>(
this.client,
'SELECT id FROM observations WHERE id = $1 AND project_id = $2 AND team_id = $3',
[input.sourceId, input.projectId, input.teamId]
);
if (!observation) {
throw new Error('observation_reindex source_id must belong to project_id and team_id');
}
if (input.serverSessionId) {
await assertSessionOwnership(this.client, input.serverSessionId, input.projectId, input.teamId);
}
}
}
export class PostgresObservationGenerationJobEventsRepository {
constructor(private client: PostgresQueryable) {}
async append(input: {
id?: string;
generationJobId: string;
projectId: string;
teamId: string;
eventType: ObservationGenerationJobEventType;
statusAfter: ObservationGenerationJobStatus;
attempt?: number;View on GitHub (pinned to d8bc9755e7)