thedotmack/claude-mem · error · Error

agent_event_id must belong to project_id and team_id

Error message

agent_event_id must belong to project_id and team_id

What it means

For 'agent_event' sources, assertAgentEventOwnership checks that the agent event id exists in agent_events scoped to project_id and team_id. If not found, the event is unknown, belongs to another tenant, or was deleted, so the source cannot be attached.

Solutions

  1. Verify the event exists: SELECT id FROM agent_events WHERE id = $1 AND project_id = $2 AND team_id = $3.
  2. Ingest/persist the agent event first, then attach the observation source using the returned id.
  3. Check that projectId/teamId match the tenant under which the event was stored.

Example fix

// before
await repo.addSource({ sourceType: 'agent_event', sourceId: uningestedEventId, ... });
// after
const evt = await agentEvents.ingest(event); // returns id in same project/team
await repo.addSource({ sourceType: 'agent_event', sourceId: evt.id, ... });
Defensive patterns

Strategy: validation

Validate before calling

const evt = await db.oneOrNone(
  'SELECT 1 FROM agent_events WHERE id = $1 AND project_id = $2 AND team_id = $3',
  [eventId, projectId, teamId]
);
if (!evt) throw new Error(`agent event ${eventId} not ingested in scope`);

Try / catch

try {
  await repo.addSource(input);
} catch (e) {
  if (e instanceof Error && e.message === 'agent_event_id must belong to project_id and team_id') {
    logger.warn('skip source: agent event not found', { eventId: input.sourceId });
    return null;
  }
  throw e;
}

Prevention

When it happens

Trigger: addSource with sourceType='agent_event' where input.sourceId (the agent event id) is not a row in agent_events for the supplied projectId/teamId.

Common situations: Ingesting events from another environment; tenant id mixups; the event was purged before observations were generated; agent event ids fabricated in a client instead of returned by the ingestion API.

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


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

Appendix: source

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

  }
  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');
  }
}

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

View on GitHub (pinned to d8bc9755e7)