mastra-ai/mastra · error
Bad Request: ${req.method} request requires a valid session
Error message
Bad Request: ${req.method} request requires a valid session ID What it means
The MCPServer's streamable-HTTP handler rejects GET or DELETE requests that arrive without a valid `mcp-session-id` header. The streamable HTTP transport is sessionful: only the initial `initialize` POST can run without a session; every subsequent request (including SSE GET streams and session-terminating DELETEs) must carry the session ID returned during initialize. The server responds with HTTP 400 and JSON-RPC code -32000.
Source
Thrown at packages/mcp/src/server/server.ts:2225
} else {
// POST request but not initialize, and no session ID
this.logger.warn('Received non-initialize POST request without session ID');
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(
JSON.stringify({
jsonrpc: '2.0',
error: {
code: -32000,
message: 'Bad Request: No valid session ID provided for non-initialize request',
},
id: (body as any)?.id ?? null, // Include original request ID if available
}),
);
}
} else {
// Non-POST request (GET/DELETE) without a session ID
this.logger.warn('Received request without session ID', { method: req.method });
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(
JSON.stringify({
jsonrpc: '2.0',
error: {
code: -32000,
message: `Bad Request: ${req.method} request requires a valid session ID`,
},
id: null,
}),
);
}
}
} catch (error) {
const mastraError = new MastraError(
{
id: 'MCP_SERVER_HTTP_CONNECTION_FAILED',
domain: ErrorDomain.MCP,
category: ErrorCategory.USER,View on GitHub (pinned to 75dd419e61)
Solutions
- Perform the `initialize` JSON-RPC POST first and capture the session ID from the `mcp-session-id` response header.
- Send that session ID back as the `mcp-session-id` header on every subsequent GET/DELETE/POST request.
- If your deployment is serverless or multi-instance, enable the serverless/stateless request path (sessionIdGenerator: undefined / handleServerlessRequest) so no session state is required.
- Verify no proxy, API gateway, or middleware strips the `mcp-session-id` header.
- If the server restarted, re-initialize the session instead of reusing a stale session ID.
Example fix
// before
curl -X GET http://localhost:4111/mcp
// after
SESSION=$(curl -s -D - -X POST http://localhost:4111/mcp -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"x","version":"1"}}}' \
| grep -i mcp-session-id | awk '{print $2}' | tr -d '\r')
curl -X GET http://localhost:4111/mcp -H "mcp-session-id: $SESSION" Defensive patterns
Strategy: validation
Validate before calling
const sid = headers['mcp-session-id'];
if (!sid && !isInitializeRequest(body)) {
throw new Error('mcp-session-id header required for non-initialize MCP requests');
} Type guard
function hasSessionId(h: Record<string, string | string[] | undefined>): h is Record<string, string> & { 'mcp-session-id': string } {
return typeof h['mcp-session-id'] === 'string' && h['mcp-session-id'].length > 0;
} Try / catch
try {
const res = await fetch(mcpUrl, { method: 'GET', headers: { 'mcp-session-id': sessionId } });
if (res.status === 400) {
const err = await res.json();
if (err.error?.code === -32000) sessionId = await reinitialize();
}
} catch (e) { /* retry with fresh initialize */ } Prevention
- Always run the initialize handshake before any GET/DELETE/POST-followup request.
- Persist the mcp-session-id header value from the initialize response and attach it to every request.
- Use an official MCP client SDK instead of raw HTTP calls so session handling is automatic.
- Check proxies/gateways for custom-header stripping rules.
- For multi-instance or serverless deployments, use the stateless/serverless server mode.
When it happens
Trigger: Calling the MCP HTTP endpoint with GET (to open an SSE stream) or DELETE (to close a session) while omitting the `mcp-session-id` header; a client that never performed the `initialize` handshake; a proxy/gateway stripping custom headers; a client reusing a base URL without persisting the session ID; server restart that lost in-memory `streamableHTTPTransports` while the client still sends its old session ID (that variant surfaces as the sibling 'No valid session ID provided' error for POST).
Common situations: Hand-rolled HTTP clients or curl probes that hit the GET endpoint directly without initializing; third-party MCP clients behind header-stripping corporate proxies; serverless/edge deployments where in-memory session maps don't survive between invocations (stateless mode should be used instead); load balancers routing follow-up requests to a different replica than the one holding the session.
Related errors
- Bad Request: No valid session ID provided for non-initialize
- JSON.stringify(errorResponse)
- Could not connect to server with any available HTTP transpor
- MCPClientServerProxy does not support HTTP transport
- MCP_SERVER_MODERN_HTTP_OPTIONS_INCOMPATIBLE
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/0e47ecae6adbd34d.
Report an issue: GitHub.