thedotmack/claude-mem · 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
Thrown by assertJobOwnership (private helper) when PostgresObservationRepository.create is called with a createdByJobId. It looks up observation_generation_jobs by (id, project_id, team_id); no row means the job is not owned by that tenant, so the observation cannot be attributed to it.
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 d768ba3643)
Solutions
- Fetch the job with jobs.getByIdForScope before setting createdByJobId.
- Omit createdByJobId when the observation is not produced by a tracked job.
- Thread the exact job id returned by jobs.create() rather than an external id.
Example fix
// before
await observations.create({ projectId, teamId, content, createdByJobId: maybeStaleId });
// after
const job = await jobs.getByIdForScope({ id: createdByJobId, projectId, teamId });
if (!job) throw new Error('job not in scope');
await observations.create({ projectId, teamId, content, createdByJobId: job.id }); Defensive patterns
Strategy: validation
Validate before calling
if (createdByJobId) { const job = await jobs.getByIdForScope({ id: createdByJobId, projectId, teamId }); if (!job) throw new Error('job not in scope'); } Prevention
- Only set createdByJobId from the worker that created the job.
- Use the DB job id, not the queue id.
When it happens
Trigger: Calling observations.create({createdByJobId}) where the job id is absent or belongs to a different project/team. Project/session ownership is checked first; this fires only when createdByJobId is set and unresolvable.
Common situations: Recording an observation with a stale job id after the job row changed; cross-tenant job id; passing a bullmq job id rather than the DB job id; creating observations outside the worker that owns the job.
Related errors
- observation_id does not exist
- agent_event_id must belong to project_id and team_id
- observation_reindex source_id must belong to project_id and
- server_session_id must match the agent_event server_session_
- observation_reindex source_id must belong to project_id and
AI-assisted analysis of thedotmack/claude-mem@d768ba3643 (2026-08-12).
Data as JSON: /api/errors/a584ffc9878cc5e6.
Report an issue: GitHub.