thedotmack/claude-mem · error · Error

generation_job_id agent_event_id must match observation…

Error message

generation_job_id agent_event_id must match observation source

What it means

For sourceType 'agent_event', the generation job row's agent_event_id must match the resolved agentEventId of the observation source being linked. A mismatch means the job was run against a different agent event than the one this source points at, so the link is rejected to keep provenance exact.

Solutions

  1. Compare the job row's agent_event_id with the event id you pass; use the job created for that exact event.
  2. Re-run generation for the correct agent event and link the new job id.
  3. Fix any parallel/retry logic that pairs jobs to events by index instead of by id.

Example fix

// before
await repo.addSource({ sourceType: 'agent_event', sourceId: evt.id, generationJobId: jobForOtherEvent.id, ... });
// after
const job = await db.one(
  'SELECT id FROM observation_generation_jobs WHERE agent_event_id = $1', [evt.id]
);
await repo.addSource({ sourceType: 'agent_event', sourceId: evt.id, generationJobId: job.id, ... });
Defensive patterns

Strategy: validation

Validate before calling

const job = await db.one(
  'SELECT agent_event_id FROM observation_generation_jobs WHERE id = $1', [generationJobId]
);
if (job.agent_event_id !== agentEventId) {
  throw new Error(`job ${generationJobId} belongs to event ${job.agent_event_id}, not ${agentEventId}`);
}

Try / catch

try {
  await repo.addSource(input);
} catch (e) {
  if (e instanceof Error && e.message === 'generation_job_id agent_event_id must match observation source') {
    logger.warn('agent_event/job mismatch', { jobId: input.generationJobId });
    return null;
  }
  throw e;
}

Prevention

When it happens

Trigger: addSource with sourceType='agent_event', a generationJobId whose row stores a different agent_event_id than the source's agent event id — e.g. two agent events handled by swapped job variables.

Common situations: Parallel generation of observations for many agent events where job results are paired to the wrong event; reprocessing events in a different order; null vs non-null agent_event_id between job and source.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of thedotmack/claude-mem@d8bc9755e7 (2026-09-17). Data as JSON: /api/errors/7b9f2507124fff5a. Report an issue: GitHub.

Appendix: source

Thrown at src/storage/postgres/observations.ts:356

    source_id: string;
    agent_event_id: string | null;
  }>(
    client,
    `
      SELECT id, source_type, source_id, agent_event_id
      FROM observation_generation_jobs
      WHERE id = $1 AND project_id = $2 AND team_id = $3
    `,
    [input.generationJobId, input.projectId, input.teamId]
  );
  if (!row) {
    throw new Error('generation_job_id must belong to project_id and team_id');
  }
  if (row.source_type !== input.sourceType || row.source_id !== input.sourceId) {
    throw new Error('generation_job_id source model must match observation source');
  }
  if (input.sourceType === 'agent_event' && row.agent_event_id !== input.agentEventId) {
    throw new Error('generation_job_id agent_event_id must match observation source');
  }
}

async function assertAgentEventOwnership(
  client: PostgresQueryable,
  agentEventId: string,
  projectId: string,
  teamId: string
): Promise<void> {
  const row = await queryOne<{ id: string }>(
    client,
    'SELECT id FROM agent_events WHERE id = $1 AND project_id = $2 AND team_id = $3',
    [agentEventId, projectId, teamId]
  );
  if (!row) {
    throw new Error('agent_event_id must belong to project_id and team_id');
  }
}

View on GitHub (pinned to d8bc9755e7)