yikart/AiToEarn · error

-32001

-32001

Error message

Session not found

What it means

The client supplied an Mcp-Session-Id header but no matching transport exists in the server's in-memory `transports` map, so the server replies 404 with JSON-RPC -32001 'Session not found'. The session was either never created on this server instance or was evicted/expired.

Source

Thrown at project/aitoearn-backend/libs/nest-mcp/src/services/mcp-streamable-http.service.ts:276

        },
      })

      // Connect transport to server
      await mcpServer.connect(transport)

      // Handle the initialization request
      await transport.handleRequest(req.raw, res.raw, body)

      this.logger.log(`[${transport.sessionId}] New session initialized`)
      return
    }

    // Case 2: Request with session ID
    if (sessionId) {
      // Check if session exists
      if (!this.transports[sessionId]) {
        this.logger.debug(`[${sessionId}] Session not found`)
        res.status(404).json({
          jsonrpc: '2.0',
          error: {
            code: -32001,
            message: 'Session not found',
          },
          id: null,
        })
        return
      }

      // Reject re-initialization attempts
      if (this.isInitializeRequest(body)) {
        res.status(400).json({
          jsonrpc: '2.0',
          error: {
            code: -32600,
            message: 'Invalid Request: Server already initialized',
          },

View on GitHub (pinned to d3aa8bea5b)

Solutions

  1. Re-run the initialize handshake to obtain a fresh Mcp-Session-Id and retry the request with it.
  2. If running multiple instances, enable sticky sessions (session affinity) at the load balancer or back the transport map with a shared store.
  3. Confirm you are hitting the stateful endpoint, not the stateless one, and that the server wasn't restarted between initialize and this call.
  4. Add client logic: on 404 'Session not found', transparently re-initialize and replay the request.

Example fix

// before
const res = await post(url, body, { headers: { 'mcp-session-id': savedId } })
// after
let res = await post(url, body, { headers: { 'mcp-session-id': savedId } })
if (res.status === 404) {
  savedId = await initialize(url)
  res = await post(url, body, { headers: { 'mcp-session-id': savedId } })
}
Defensive patterns

Strategy: retry

Validate before calling

const sid = getSessionId()
if (!sid || sidExpiresBefore(Date.now())) {
  await reinitialize() // obtain fresh Mcp-Session-Id before calling
}

Type guard

function hasSessionId(headers: Record<string, string | undefined>): headers is Record<string, string> & { 'mcp-session-id': string } {
  return typeof headers['mcp-session-id'] === 'string' && headers['mcp-session-id'].length > 0
}

Try / catch

const res = await post(url, body, sessionHeaders)
if (res.status === 404 && (await res.clone().json())?.error?.message === 'Session not found') {
  sessionId = await initialize(url) // re-handshake then retry once
  return post(url, body, sessionHeaders)
}

Prevention

When it happens

Trigger: POSTing with an Mcp-Session-Id that the current server instance never issued: after server restart, against a different replica behind a load balancer, after the transport was closed/deleted, or a typo'd/stale session id from an old client run.

Common situations: Stateful deployments scaled horizontally without sticky sessions, server restarted mid-conversation while the client kept its old session id, session TTL/cleanup deleting idle transports, or pointing a saved session id at the stateless endpoint.

Related errors


AI-assisted analysis of yikart/AiToEarn@d3aa8bea5b (2026-08-31). Data as JSON: /api/errors/b189546180e19cb0. Report an issue: GitHub.