linshenkx/prompt-optimizer · error

-32000

-32000

Error message

Bad Request: No valid session ID provided

What it means

This JSON-RPC error (code -32000) is returned as HTTP 400 by the MCP server's POST /mcp handler when the request is neither a valid InitializeRequest nor carries a known mcp-session-id. It mirrors the Streamable HTTP transport's requirement that all non-initialize requests reference an existing session.

Source

Thrown at packages/mcp-server/src/index.ts:424

            enableDnsRebindingProtection: false
          });

          // 清理传输实例
          httpTransport.onclose = () => {
            if (httpTransport.sessionId) {
              delete transports[httpTransport.sessionId];
            }
          };

          // 为每个会话创建独立的服务器实例
          const { server } = await createServerInstance(config);
          await setupServerHandlers(server, coreServices);

          // 连接到 MCP 服务器
          await server.connect(httpTransport);
        } else {
          // 无效请求
          res.status(400).json({
            jsonrpc: '2.0',
            error: {
              code: -32000,
              message: 'Bad Request: No valid session ID provided',
            },
            id: null,
          });
          return;
        }

        // 处理请求
        await httpTransport.handleRequest(req, res, req.body);
      });

      // 处理 GET 请求(服务器到客户端通知,通过 SSE)
      app.get('/mcp', async (req, res) => {
        const sessionId = req.headers['mcp-session-id'] as string | undefined;
        if (!sessionId || !transports[sessionId]) {

View on GitHub (pinned to 3e677b1d9f)

Solutions

  1. Make the client perform the initialize handshake first and cache the mcp-session-id header returned by the server, sending it on every subsequent request.
  2. If the server restarted (sessions are in-memory), have the client re-initialize on 400/-32000 and retry.
  3. If a proxy/gateway sits in front, verify it forwards the mcp-session-id request and response headers.

Example fix

// before
await fetch(MCP_URL + '/mcp', { method: 'POST', body: JSON.stringify({ method: 'tools/list' }) })
// after
const init = await fetch(MCP_URL + '/mcp', { method: 'POST', body: JSON.stringify(initRequest) })
const sessionId = init.headers.get('mcp-session-id')
await fetch(MCP_URL + '/mcp', { method: 'POST', headers: { 'mcp-session-id': sessionId }, body: JSON.stringify({ method: 'tools/list' }) })
Defensive patterns

Strategy: retry

Validate before calling

if (!sessionId) {
  const initRes = await fetch(MCP_URL + '/mcp', {
    method: 'POST',
    headers: { 'content-type': 'application/json', accept: 'application/json, text/event-stream' },
    body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'initialize', params: { protocolVersion: '2025-03-26', capabilities: {}, clientInfo: { name: 'my-client', version: '1.0.0' } } })
  })
  sessionId = initRes.headers.get('mcp-session-id')
}

Type guard

const isNoSessionError = (body: any): boolean =>
  body?.error?.code === -32000 && /No valid session ID/.test(body?.error?.message ?? '')

Try / catch

try { await postRpc(method, params) } catch (e) { if (isNoSessionError(e)) { await reinitialize(); await postRpc(method, params) } else throw e }

Prevention

When it happens

Trigger: POST /mcp without an mcp-session-id header and with a body that is not a valid initialize request (e.g. a tools/call sent before initialize, a malformed initialize, or a client that lost/never stored the session ID).

Common situations: MCP client (Claude Desktop, Cursor, custom SDK client) reconnecting after a server restart — the old session ID is gone from the in-memory transports map; clients that skip the initialize handshake; proxies stripping the mcp-session-id header.

Related errors


AI-assisted analysis of linshenkx/prompt-optimizer@3e677b1d9f (2026-08-27). Data as JSON: /api/errors/2741aaa37e666fcf. Report an issue: GitHub.