thedotmack/claude-mem · error · Error
generation_job_id must belong to project_id and team_id
Error message
generation_job_id must belong to project_id and team_id
What it means
append() on the job-events repository inserts a job event but only succeeds if the generation_job_id belongs to the given project_id and team_id; if the INSERT ... RETURNING matches no row, it throws. This prevents appending events to jobs outside the caller's scope.
Solutions
- Confirm the generation job exists under the same project_id/team_id before appending events
- Refetch the job by id scoped to your project/team and use that row's id
- Check for concurrent cancellation/deletion of the job and handle the not-found case gracefully
- Verify you are not passing a different entity's id (observation/event) as generationJobId
Example fix
// before
await jobEvents.append({ generationJobId: someId, projectId, teamId, type: 'note' });
// after
const job = await jobs.getByIdForScope(someId, projectId, teamId);
if (!job) throw new Error('job not in scope');
await jobEvents.append({ generationJobId: job.id, projectId, teamId, type: 'note' }); Defensive patterns
Strategy: validation
Validate before calling
const job = await client.query('SELECT 1 FROM observation_generation_jobs WHERE id=$1 AND project_id=$2 AND team_id=$3', [jobId, projectId, teamId]);
if (job.rowCount === 0) throw new Error('generation job not in scope'); Try / catch
try {
await jobEvents.append(input);
} catch (err) {
if (err instanceof Error && err.message.includes('generation_job_id must belong')) {
logger.warn({ jobId: input.generationJobId }, 'job missing or out of scope; skipping event append');
return null;
}
throw err;
} Prevention
- Refetch the job scoped to your project/team before appending events
- Handle concurrent job deletion/cancellation by treating append-not-found as benign
- Distinguish job ids from observation/event ids clearly in your data model
When it happens
Trigger: Calling append() (or markGenerationFailed / persistGeneratedObservations / retryGenerationJob / cancelGenerationJob / txResult / txResults) with a generationJobId that does not exist, or exists under a different project_id/team_id than the input scope.
Common situations: Worker processing a job whose row was concurrently deleted or cancelled; cross-tenant id reuse after data deletion; passing the observation id instead of the job id; stale job id from a previous database reset.
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
- observation_id does not exist
- observation_reindex source_id must belong to project_id and…
- agent_event_id must belong to project_id and team_id
- agent_event source_id must equal agent_event_id
AI-assisted analysis of thedotmack/claude-mem@d8bc9755e7 (2026-09-17).
Data as JSON: /api/errors/63f9ebe2c8e01e71.
Report an issue: GitHub.
Appendix: source
Thrown at src/storage/postgres/generation-jobs.ts:342
FROM observation_generation_jobs jobs
WHERE jobs.id = $2
AND jobs.project_id = $3
AND jobs.team_id = $8
RETURNING observation_generation_job_events.*
`,
[
input.id ?? newId(),
input.generationJobId,
input.projectId,
input.eventType,
input.statusAfter,
input.attempt ?? 0,
JSON.stringify(input.details ?? {}),
input.teamId
]
);
if (!row) {
throw new Error('generation_job_id must belong to project_id and team_id');
}
return mapJobEventRow(row!);
}
async listByJobForScope(input: {
generationJobId: string;
projectId: string;
teamId: string;
}): Promise<PostgresObservationGenerationJobEvent[]> {
const result = await this.client.query<JobEventRow>(
`
SELECT events.*
FROM observation_generation_job_events events
INNER JOIN observation_generation_jobs jobs ON jobs.id = events.generation_job_id
WHERE events.generation_job_id = $1 AND jobs.project_id = $2 AND jobs.team_id = $3
ORDER BY events.created_at ASC
`,
[input.generationJobId, input.projectId, input.teamId]View on GitHub (pinned to d8bc9755e7)