thedotmack/claude-mem · warning
session linkage lookup failed; storing event unlinked
Error message
session linkage lookup failed; storing event unlinked
What it means
In the server's event ingestion route, each event is linked to a server session via repo.findIdByContentSessionId (results memoized per request in a promise cache). If the Postgres lookup rejects, the route logs this warning, resolves the lookup to null, and stores the event unlinked (no serverSessionId) rather than failing the request. Linkage must be repaired later by a backfill.
Source
Thrown at src/server/routes/v1/ServerV1PostgresRoutes.ts:1216
const platformScope = this.sessionLookupPlatformScope(rawBodies[index]);
const hasPlatformScope = Object.prototype.hasOwnProperty.call(platformScope, 'platformSource');
const cacheKey = JSON.stringify([
input.projectId,
teamId,
input.contentSessionId,
hasPlatformScope,
hasPlatformScope ? platformScope.platformSource ?? null : null,
]);
let lookup = lookups.get(cacheKey);
if (!lookup) {
lookup = repo.findIdByContentSessionId({
contentSessionId: input.contentSessionId,
projectId: input.projectId,
teamId,
...platformScope,
}).catch((err: unknown) => {
logger.warn('HTTP', 'session linkage lookup failed; storing event unlinked', {
error: err instanceof Error ? err.message : String(err),
});
return null;
});
lookups.set(cacheKey, lookup);
}
const linkedId = await lookup;
if (linkedId) input.serverSessionId = linkedId;
}));
}
private sessionLookupPlatformScope(body: unknown): { platformSource?: string | null } {
if (!body || typeof body !== 'object') return {};
if (!Object.prototype.hasOwnProperty.call(body, 'platformSource')) return {};
const value = (body as { platformSource?: unknown }).platformSource;
return {View on GitHub (pinned to e2d1df569a)
Solutions
- Read the error field in the warn line: it carries the underlying Postgres error (connection, timeout, or SQL failure)
- For transient causes (restart, failover) take no immediate action: events are stored unlinked, so run the session-link backfill or repair job to re-link them
- If statement timeouts repeat, raise statement_timeout or add the index the lookup needs on (content_session_id, project_id, team_id)
- After migrations, verify the sessions table shape still matches findIdByContentSessionId
Example fix
// before
const linkedId = await repo.findIdByContentSessionId({ contentSessionId, projectId, teamId });
input.serverSessionId = linkedId ?? undefined;
// after: degrade gracefully and leave a repairable linkage gap
let linkedId: string | null = null;
try {
linkedId = await repo.findIdByContentSessionId({ contentSessionId, projectId, teamId });
} catch (error) {
logger.warn('HTTP', 'session linkage lookup failed; storing event unlinked', {
error: error instanceof Error ? error.message : String(error),
});
}
input.serverSessionId = linkedId ?? undefined; // a later backfill links it Defensive patterns
Strategy: fallback
Validate before calling
// Before a bulk ingestion run, verify the sessions lookup path is healthy: await pool.query( 'SELECT 1 FROM sessions WHERE content_session_id = $1 LIMIT 1', ['probe'], ); // If this throws, pause ingestion: events would be stored unlinked.
Type guard
function isPgDatabaseError(error: unknown): error is { code: string; detail?: string; message: string } {
return typeof error === 'object' && error !== null && 'code' in error
&& typeof (error as { code: unknown }).code === 'string';
} Try / catch
let lookup: Promise<string | null> = repo.findIdByContentSessionId({ ... });
lookup = lookup.catch((error: unknown) => {
logger.warn('HTTP', 'session linkage lookup failed; storing event unlinked', {
error: error instanceof Error ? error.message : String(error),
});
return null; // degrade to unlinked storage; a backfill re-links later
}); Prevention
- Size the Postgres pool for ingestion bursts so the lookup does not reject under load
- Set statement_timeout above the p95 of the session-linkage query
- Deploy schema migrations before the API version that depends on them
- Schedule the session-link backfill so unlinked events are repaired, not just tolerated
When it happens
Trigger: POSTing events to the v1 ingestion endpoint while Postgres has a transient failure (connection drop, pool exhaustion, statement timeout, failover) or after a schema change to the sessions table that breaks the lookup query.
Common situations: PgBouncer or RDS idle timeouts reaping connections mid-request; traffic bursts exhausting the pool; a migration deployed while traffic flows; database restart during a deploy window.
Understand the failure class
Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.
Related errors
- Cannot bootstrap server API key: CLAUDE_MEM_SERVER_DATABASE_
- Postgres requires CLAUDE_MEM_SERVER_DATABASE_URL
- observation generation job status transition was not applied
- agent_event source_id must belong to project_id and team_id
- server_session_id must match the agent_event server_session_
AI-assisted analysis of thedotmack/claude-mem@e2d1df569a (2026-08-20).
Data as JSON: /api/errors/77cce5ed1f5d5fa0.
Report an issue: GitHub.