thedotmack/claude-mem · error

agent_event source_id must belong to project_id and team_id

Error message

agent_event source_id must belong to project_id and team_id

What it means

Thrown by validateSource when creating/scoping an observation_generation_job with sourceType 'agent_event' but the referenced agent_event row doesn't exist for the given eventId+project_id+team_id, or when sourceId was provided alongside a differing agentEventId. This enforces tenancy: the source event must belong to the same project and team as the job.

Source

Thrown at src/storage/postgres/generation-jobs.ts:261

  private async validateSource(input: {
    projectId: string;
    teamId: string;
    sourceType: ObservationGenerationJobSourceType;
    sourceId: string;
    agentEventId?: string | null;
    serverSessionId?: string | null;
  }): Promise<void> {
    await assertProjectOwnership(this.client, input.projectId, input.teamId);
    if (input.sourceType === 'agent_event') {
      const eventId = input.agentEventId ?? input.sourceId;
      const row = await queryOne<{ id: string; server_session_id: string | null }>(
        this.client,
        'SELECT id, server_session_id FROM agent_events WHERE id = $1 AND project_id = $2 AND team_id = $3',
        [eventId, input.projectId, input.teamId]
      );
      if (!row || input.sourceId !== eventId) {
        throw new Error('agent_event source_id must belong to project_id and team_id');
      }
      if (input.serverSessionId) {
        await assertSessionOwnership(this.client, input.serverSessionId, input.projectId, input.teamId);
        if (row.server_session_id && row.server_session_id !== input.serverSessionId) {
          throw new Error('server_session_id must match the agent_event server_session_id');
        }
      }
      return;
    }

    if (input.sourceType === 'session_summary') {
      const sessionId = input.serverSessionId ?? input.sourceId;
      await assertSessionOwnership(this.client, sessionId, input.projectId, input.teamId);
      if (input.sourceId !== sessionId) {
        throw new Error('session_summary source_id must equal server_session_id');
      }
      return;
    }

View on GitHub (pinned to d768ba3643)

Solutions

  1. Verify the agent_event exists for the same project_id and team_id before creating the job (SELECT id FROM agent_events WHERE id=? AND project_id=? AND team_id=?).
  2. Ensure sourceId and agentEventId refer to the same event (or omit agentEventId when it equals sourceId).
  3. Confirm project_id/team_id on the request match the event's ownership; reject cross-tenant references early.
  4. If the event was deleted, either re-create it or choose a valid source.

Example fix

// before
await repo.create({ sourceType: 'agent_event', sourceId, agentEventId: otherId, projectId, teamId });

// after
const eventId = agentEventId ?? sourceId;
if (sourceId !== eventId) throw new Error('sourceId must match agentEventId');
const owned = await queryOne('SELECT id FROM agent_events WHERE id=$1 AND project_id=$2 AND team_id=$3', [eventId, projectId, teamId]);
if (!owned) throw new Error('agent_event not owned by project/team');
await repo.create({ sourceType: 'agent_event', sourceId, projectId, teamId });
Defensive patterns

Strategy: validation

Validate before calling

// Pre-validate ownership and id consistency
const eventId = input.agentEventId ?? input.sourceId;
if (input.sourceId !== eventId) throw new Error('sourceId must equal agentEventId');
const row = await queryOne(
  'SELECT id FROM agent_events WHERE id = $1 AND project_id = $2 AND team_id = $3',
  [eventId, input.projectId, input.teamId]
);
if (!row) throw new Error('agent_event not owned by project/team');

Try / catch

try {
  await repo.create(input);
} catch (err) {
  if (err instanceof Error && /source_id must belong to project_id and team_id/.test(err.message)) {
    return res.status(400).json({ error: 'Agent event not found for this project/team' });
  }
  throw err;
}

Prevention

When it happens

Trigger: Job creation/input with sourceType 'agent_event' where (eventId = agentEventId ?? sourceId) has no row in agent_events for that project/team, or input.sourceId !== eventId (the two identifiers disagree).

Common situations: Caller passed an agentEventId from a different project/team (cross-tenant); typo in sourceId/agentEventId; the agent_event was deleted before job creation; mixing sourceId and agentEventId inconsistently.

Related errors


AI-assisted analysis of thedotmack/claude-mem@d768ba3643 (2026-08-12). Data as JSON: /api/errors/543b9e80d66fb088. Report an issue: GitHub.