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
assertJobOwnership looks up observation_generation_jobs by id scoped to project_id and team_id. If no row matches, the generation job id passed to create does not exist in that tenant scope, so the observation creation is rejected. This ensures observations can only reference generation jobs owned by the same project and team.
Solutions
- Query observation_generation_jobs with the id plus exact projectId/teamId to confirm the job exists.
- Ensure projectId/teamId passed to create come from the same tenant context that created the job.
- If the job was deleted, re-create the generation job before creating observations that reference it.
- Remove generationJobId if the observation was not actually produced by a generation job.
Example fix
// before
await repo.create({ ..., generationJobId: job.id, projectId: otherProjectId });
// after
const job = await jobs.get({ id: jobId, projectId, teamId });
if (!job) throw new Error(`generation job ${jobId} not found in project ${projectId}`);
await repo.create({ ..., generationJobId: job.id, projectId, teamId }); Defensive patterns
Strategy: try-catch
Validate before calling
const job = await db.oneOrNone( 'SELECT id FROM observation_generation_jobs WHERE id = $1 AND project_id = $2 AND team_id = $3', [generationJobId, projectId, teamId] ); if (!job) return; // skip creating an observation tied to a missing job
Try / catch
try {
await repo.create(input);
} catch (e) {
if (e instanceof Error && e.message === 'generation_job_id must belong to project_id and team_id') {
logger.error('generation job out of scope', { generationJobId: input.generationJobId, projectId, teamId });
return null;
}
throw e;
} Prevention
- Create observations and their generation job in one transaction.
- Thread projectId/teamId through the pipeline from one request context.
- Retention/purge jobs should not delete generation jobs still referenced by in-flight pipelines.
When it happens
Trigger: Passing a generationJobId to the observations create path where the job id is wrong, belongs to another project/team, or the job row was purged before observations were created.
Common situations: Replaying persisted job payloads against a different database/environment; multi-tenant mixups where projectId/teamId come from a different request context; retention cleanup deleted jobs while observations were still being generated.
Understand the failure class
Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.
Related errors
- agent_event_id must belong to project_id and team_id
- manual observation sources cannot be linked to a…
- observation_id does not exist
- observation_reindex source_id must belong to project_id and…
- agent_event source_id must belong to project_id and team_id
AI-assisted analysis of thedotmack/claude-mem@d8bc9755e7 (2026-09-17).
Data as JSON: /api/errors/a584ffc9878cc5e6.
Report an issue: GitHub.
Appendix: source
Thrown at src/storage/postgres/observations.ts:316
}): string {
return `generation:v1:${input.generationJobId}:${input.parsedObservationIndex}:${deterministicKey([
canonicalJson(input.content.trim())
])}`;
}
async function assertJobOwnership(
client: PostgresQueryable,
generationJobId: string,
projectId: string,
teamId: string
): Promise<void> {
const row = await queryOne<{ id: string }>(
client,
'SELECT id FROM observation_generation_jobs WHERE id = $1 AND project_id = $2 AND team_id = $3',
[generationJobId, projectId, teamId]
);
if (!row) {
throw new Error('generation_job_id must belong to project_id and team_id');
}
}
async function assertGenerationJobMatchesSource(
client: PostgresQueryable,
input: {
generationJobId: string;
projectId: string;
teamId: string;
sourceType: ObservationSourceType;
sourceId: string;
agentEventId: string | null;
}
): Promise<void> {
if (input.sourceType === 'manual') {
throw new Error('manual observation sources cannot be linked to a generation_job_id');
}
View on GitHub (pinned to d8bc9755e7)