thedotmack/claude-mem · error · ServerClientError
invalid_response
invalid_response
Error message
sessionId is required for endSession
What it means
Thrown by ServerClient.endSession() when input.sessionId is falsy. The kind is 'invalid_response' (a client-side guard, despite the name) and fires before the POST /v1/sessions/<id>/end request is built, so no network call occurs on failure.
Source
Thrown at src/services/hooks/server-client.ts:222
this.baseUrl = stripTrailingSlash(config.serverBaseUrl);
this.apiKey = config.apiKey;
this.timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
}
async startSession(input: ServerStartSessionRequest): Promise<ServerStartSessionResponse> {
const body = this.buildStartSessionPayload(input);
return this.request<ServerStartSessionResponse>('POST', '/v1/sessions/start', body);
}
async recordEvent(input: ServerRecordEventRequest): Promise<ServerRecordEventResponse> {
const body = this.buildEventPayload(input);
const path = input.generate === false ? '/v1/events?generate=false' : '/v1/events';
return this.request<ServerRecordEventResponse>('POST', path, body);
}
async endSession(input: ServerEndSessionRequest): Promise<ServerEndSessionResponse> {
if (!input.sessionId) {
throw new ServerClientError('invalid_response', 'sessionId is required for endSession');
}
return this.request<ServerEndSessionResponse>(
'POST',
`/v1/sessions/${encodeURIComponent(input.sessionId)}/end`,
{},
);
}
// Phase 8 — direct observation insert (MCP `observation_add`). Calls
// `/v1/memories`, which is the canonical write path that MUST NOT enqueue
// a generation job. Anti-pattern guard for plan line 770: never duplicate
// generation logic in MCP tools.
async addObservation(
input: ServerAddObservationRequest,
): Promise<ServerAddObservationResponse> {
return this.request<ServerAddObservationResponse>(
'POST',
'/v1/memories',View on GitHub (pinned to d768ba3643)
Solutions
- Capture and pass the sessionId returned by startSession() into endSession().
- Guard the end call: only invoke endSession when you have a non-empty sessionId.
- If session start failed, skip the end call entirely rather than passing an empty id.
Example fix
// before
await client.endSession({ sessionId: '' });
// throws ServerClientError(invalid_response, 'sessionId is required for endSession')
// after
const { sessionId } = await client.startSession({ ... });
// ... later, only if we got one
if (sessionId) {
await client.endSession({ sessionId });
} Defensive patterns
Strategy: validation
Validate before calling
if (!input?.sessionId) {
throw new Error('endSession: sessionId is required (capture it from startSession)');
} Type guard
function hasSessionId(input: unknown): input is { sessionId: string } {
return typeof (input as any)?.sessionId === 'string' && (input as any).sessionId.length > 0;
} Prevention
- Capture and propagate the sessionId from startSession to endSession.
- Skip the end call when start failed rather than passing an empty id.
- Pass the id string, not the whole start response object.
When it happens
Trigger: Calling endSession({ sessionId: '' }), endSession({}) (sessionId undefined), or with a null/whitespace id. The guard is a simple truthiness check on input.sessionId.
Common situations: Hook handler tries to end a session that was never started (no start response captured); sessionId variable was undefined after a failed startSession; caller passes the whole start response object instead of its id field.
Related errors
- server_session_id must belong to project_id and team_id
- Invalid CLAUDE_MEM_WORKER_PORT in settings.json: missing
- Invalid CLAUDE_MEM_WORKER_PORT in settings.json: ${rawPort}
- Unknown Claude model: ${options.model}. Allowed: ${[...allow
- CLAUDE_MEM_QUEUE_ENGINE is not "bullmq"
AI-assisted analysis of thedotmack/claude-mem@d768ba3643 (2026-08-12).
Data as JSON: /api/errors/7a4dbfb606dfd749.
Report an issue: GitHub.