thedotmack/claude-mem · error

session_summary source_id must equal server_session_id

Error message

session_summary source_id must equal server_session_id

What it means

Thrown by validateSource when sourceType is 'session_summary'. The resolved sessionId is serverSessionId ?? sourceId; if the caller supplies both a sourceId and a serverSessionId that are not identical, the job is rejected. session_summary sources are keyed entirely by a single server session, so the two fields must collapse to the same value.

Source

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

        [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]
    );
    if (!observation) {
      throw new Error('observation_reindex source_id must belong to project_id and team_id');
    }
    if (input.serverSessionId) {
      await assertSessionOwnership(this.client, input.serverSessionId, input.projectId, input.teamId);
    }
  }
}

View on GitHub (pinned to d768ba3643)

Solutions

  1. Pass sourceId and serverSessionId as the same value (the canonical server session id).
  2. Pass only sourceId (serverSessionId omitted); validateSource uses serverSessionId ?? sourceId so it resolves correctly.
  3. Pass only serverSessionId and set sourceId to the same session id.

Example fix

// before
await jobs.create({ sourceType:'session_summary', sourceId: groupId, serverSessionId: sessionId, ... });
// after
await jobs.create({ sourceType:'session_summary', sourceId: sessionId, serverSessionId: sessionId, ... });
Defensive patterns

Strategy: validation

Validate before calling

function normalizeSessionSummarySource(sourceId, serverSessionId) {
  if (sourceId != null && serverSessionId != null && sourceId !== serverSessionId) {
    throw new TypeError('sourceId and serverSessionId must be identical for session_summary');
  }
  const sessionId = serverSessionId ?? sourceId;
  return { sourceId: sessionId, serverSessionId: sessionId };
}

Type guard

function isSessionSummaryConsistent(input) {
  if (input.sourceType !== 'session_summary') return true;
  const both = input.sourceId != null && input.serverSessionId != null;
  return !both || input.sourceId === input.serverSessionId;
}

Prevention

When it happens

Trigger: Calling repository.create() with sourceType:'session_summary' where sourceId and serverSessionId are both set and differ. AssertSessionOwnership runs first, so both must exist in scope before this guard triggers.

Common situations: Orchestrator defaults serverSessionId to a global value while sourceId is the real session; UI passing a derived/session-group id as sourceId alongside the real session id; refactor that started populating both fields where only one was used before.

Related errors


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