thedotmack/claude-mem · error · Error

server_session_id must match the agent_event…

Error message

server_session_id must match the agent_event server_session_id

What it means

When creating a generation job with sourceType 'agent_event' and an explicit serverSessionId, validateSource checks that the referenced agent_event's stored server_session_id matches the caller-supplied one. If the event is already bound to a different session, the mismatch is rejected. This keeps job/session/event linkage consistent.

Solutions

  1. Pass the same serverSessionId that the agent_event is bound to (read it from the event row first)
  2. Omit serverSessionId if you do not need to assert session ownership on the job
  3. Use an event id belonging to the current session instead of an older one
  4. Verify the event was not re-bound to a different session between fetch and create

Example fix

// before
await jobs.create({ sourceType: 'agent_event', sourceId: eventId, serverSessionId: currentSessionId });
// after
const event = await eventsRepository.getById(eventId);
await jobs.create({ sourceType: 'agent_event', sourceId: eventId, serverSessionId: event.serverSessionId ?? currentSessionId });
Defensive patterns

Strategy: validation

Validate before calling

if (input.serverSessionId) {
  const ev = await client.query('SELECT server_session_id FROM agent_events WHERE id=$1', [input.sourceId]);
  if (ev.rows[0]?.server_session_id && ev.rows[0].server_session_id !== input.serverSessionId) {
    throw new Error('serverSessionId does not match event');
  }
}

Try / catch

try {
  await jobs.create(input);
} catch (err) {
  if (err instanceof Error && err.message.includes('server_session_id must match')) {
    // fall back to the event's own session or drop serverSessionId and retry
    await jobs.create({ ...input, serverSessionId: undefined });
  } else throw err;
}

Prevention

When it happens

Trigger: create() with sourceType 'agent_event', a serverSessionId provided, and the agent_event row's server_session_id is non-null and different from input.serverSessionId (the event is bound to another session).

Common situations: Caller passes the current session id while the event originated in an earlier session; a session restart changed ids but the caller reused a stale event id; race where the event was bound to a session after the caller read it.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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

Appendix: source

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

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

    const observation = await queryOne<{ id: string }>(
      this.client,
      'SELECT id FROM observations WHERE id = $1 AND project_id = $2 AND team_id = $3',
      [input.sourceId, input.projectId, input.teamId]

View on GitHub (pinned to d8bc9755e7)