Bad Request: No valid session ID provided

Node.js · updated 2026-11-11

Returned with HTTP 400 by MCP servers built on the TypeScript SDK's StreamableHTTPServerTransport in stateful mode, usually by the session-map code from the SDK's own examples: a request carries an Mcp-Session-Id the process has never seen. The client (Claude Code, Cursor, the SDK's StreamableHTTPClientTransport) reports "Streamable HTTP error: Error POSTing to endpoint" with that body. The transport itself answers an unknown session with 404 "Session not found", a missing header with "Bad Request: Mcp-Session-Id header is required", and a second initialize on one shared transport with -32600 "Invalid Request: Server already initialized".

StreamableHTTPError: Streamable HTTP error: Error POSTing to endpoint: {"jsonrpc":"2.0","error":{"code":-32000,"message":"Bad Request: No valid session ID provided"},"id":null}
    at StreamableHTTPClientTransport.send (node_modules/@modelcontextprotocol/sdk/dist/esm/client/streamableHttp.js)
  code: 400

Quick fix

If your tools don't need per-session state, run the server stateless: pass sessionIdGenerator: undefined and create a new McpServer and transport for every request. Any instance can then answer any request, across restarts and deploys. If you do need sessions, send each session to the process that created it (sticky routing on the Mcp-Session-Id header) or keep the session state in a shared store.

  1. Check where requests land: more than one instance, a serverless function, or a restart between requests all lose an in-memory session map.
  2. For tools that are plain request and response (most are), switch to stateless mode as in the code below.
  3. Never create one transport at startup and share it across clients: the second client's initialize fails with "Server already initialized".
  4. If you keep sessions, configure sticky routing on Mcp-Session-Id at the load balancer, and expect clients to reconnect after a deploy.
  5. Make sure proxies pass the Mcp-Session-Id header through, in both directions.
typescript
import express from 'express';
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';

const app = express();
app.use(express.json());

// Stateless: a fresh server and transport per request, so any instance can answer.
app.post('/mcp', async (req, res) => {
  const server = new McpServer({ name: 'orders', version: '1.0.0' });
  registerTools(server);
  const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });
  res.on('close', () => { transport.close(); server.close(); });
  await server.connect(transport);
  await transport.handleRequest(req, res, req.body);
});

app.listen(3000);
Fix it with your coding agent

Paste this into Claude Code, Cursor or Codex in the project that throws it. The agent reads this guide in plain text and applies the fix that matches your stack.

prompttext
Read https://runheed.com/errors/mcp-no-valid-session-id-provided.md and fix this error in this project.

Why it happens

More than one instance

Initialize went to one process and the next request to another, behind a load balancer, on autoscaled containers or on serverless functions.

A restart or deploy

The session map lived in memory, so every client that was connected holds a session ID the new process doesn't know.

One transport shared by every client

A single stateful transport created at startup accepts only one initialize; later clients get "Server already initialized" and then session errors.

The header gets lost

A proxy, CDN or CORS setup that drops Mcp-Session-Id, or a hand-written client that doesn't send it back.

Other fixes

  • Stateless mode can't send server-initiated messages between requests (such as a notification on a separate stream). If you need those, use sessions with sticky routing.
  • In the browser, expose the header with CORS: Access-Control-Expose-Headers: Mcp-Session-Id.
  • The v2 server package (@modelcontextprotocol/server) returns the same messages.

Same error, other wording

  • Streamable HTTP error: Error POSTing to endpoint (HTTP 400): Bad Request: No valid session ID provided
  • Session not found (404, code -32001)
  • Bad Request: Mcp-Session-Id header is required
  • Invalid Request: Server already initialized

Let your coding agent catch the next one

Your MCP server answers this with an HTTP error response and throws nothing, so unless your handler reports it Runheed doesn't record it. What it does record is the exceptions your running server code throws (route handlers, server rendering, API errors and background jobs) with the stack trace and the breadcrumbs before them, and serves those errors to Claude Code, Cursor or any MCP client.

Setup for Node.js and Express or Vercel Functions, then have your agent fix and resolve it.

Building an MCP server? Track the errors your tools raise, in TypeScript or Python, so a failing tool shows up with its line instead of only in your users' client logs.

Related errors

More in MCP servers and clients

References: MCP specification: Streamable HTTP transport, session management, MCP TypeScript SDK repository (server examples and transports)

Spotted a mistake? Email hello@greatwork.company. About Runheed.