abhigyanpatwari/GitNexus · error

-32000

-32000

Error message

First request must be initialize. No session ID provided.

What it means

Thrown by the GitNexus MCP streamable-HTTP endpoint (POST /mcp) when a request arrives with no mcp-session-id header and its JSON-RPC body (single message or batch) contains no initialize request. The MCP protocol requires the very first request on a fresh connection to be initialize, because that is the only path that allocates a Server + Transport pair and assigns a session ID. Rejecting anything else up front prevents orphaned Server instances that the TTL sweep could never reclaim.

Source

Thrown at gitnexus/src/mcp/http-transport.ts:276

      const session = sessions.get(sessionId)!;
      session.lastActivity = Date.now();
      await session.transport.handleRequest(req, res, req.body);
    } else if (sessionId) {
      // Unknown / expired session ID — tell the client to re-initialize (per MCP spec).
      res.status(404).json({
        jsonrpc: '2.0',
        error: { code: -32001, message: 'Session not found. Re-initialize.' },
        id: null,
      });
    } else if (req.method === 'POST') {
      // No session ID — new client. Only accept initialize requests to avoid
      // orphaned Server instances that can never be reclaimed by the TTL sweep.
      // Use the SDK's isInitializeRequest so a single-element JSON-RPC batch is
      // recognised too, rather than a brittle `body.method === 'initialize'` check.
      const body = req.body as unknown;
      const messages = Array.isArray(body) ? body : [body];
      if (!messages.some(isInitializeRequest)) {
        res.status(400).json({
          jsonrpc: '2.0',
          error: {
            code: -32000,
            message: 'First request must be initialize. No session ID provided.',
          },
          id: null,
        });
        return;
      }

      // Reject when the session cap is reached — prevents memory exhaustion via
      // an initialize flood (each session holds a live Server + Transport).
      if (sessions.size >= MAX_SESSIONS) {
        res.status(503).json({
          jsonrpc: '2.0',
          error: { code: -32000, message: 'Server at session capacity. Try again later.' },
          id: null,
        });

View on GitHub (pinned to aac7515d2a)

Solutions

  1. Send a proper JSON-RPC initialize request first: POST /mcp with Content-Type: application/json and Accept: application/json, text/event-stream, body {jsonrpc:'2.0',id:1,method:'initialize',params:{protocolVersion, capabilities:{}, clientInfo:{...}}}
  2. Read the mcp-session-id response header and include it as a request header on every subsequent POST/GET/DELETE to /mcp
  3. If using the MCP TypeScript SDK, let StreamableHTTPClientTransport manage the handshake and session header instead of hand-rolling fetch calls
  4. If batching the first request, make sure the array contains the initialize message (the server checks Array.isArray(body) ? body : [body] with the SDK's isInitializeRequest)

Example fix

// before
await fetch(`${base}/mcp`, {
  method: 'POST',
  headers: { 'content-type': 'application/json' },
  body: JSON.stringify({ jsonrpc: '2.0', method: 'tools/list', id: 1 }),
}); // 400 -32000

// after
const initRes = await fetch(`${base}/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: 'ci', version: '1.0.0' } },
  }),
});
const sessionId = initRes.headers.get('mcp-session-id');
await fetch(`${base}/mcp`, {
  method: 'POST',
  headers: { 'content-type': 'application/json', accept: 'application/json, text/event-stream', 'mcp-session-id': sessionId! },
  body: JSON.stringify({ jsonrpc: '2.0', method: 'notifications/initialized' }),
});
Defensive patterns

Strategy: validation

Validate before calling

// Before the first POST to /mcp, verify the message is an initialize request.
function buildFirstMessage(): { jsonrpc: '2.0'; id: number; method: string; params?: unknown } {
  return { jsonrpc: '2.0', id: 1, method: 'initialize', params: { protocolVersion: '2025-03-26', capabilities: {}, clientInfo: { name: 'my-client', version: '1.0.0' } } };
}
const first = buildFirstMessage();
if (first.method !== 'initialize') throw new Error('first message must be initialize');
const res = await fetch(`${base}/mcp`, { method: 'POST', headers: { 'content-type': 'application/json', accept: 'application/json, text/event-stream' }, body: JSON.stringify(first) });
const sessionId = res.headers.get('mcp-session-id');
if (!sessionId) throw new Error(`handshake rejected: HTTP ${res.status} ${await res.text()}`);

Type guard

function isInitializeRequest(m: unknown): m is { jsonrpc: '2.0'; method: 'initialize' } {
  return (
    typeof m === 'object' && m !== null &&
    (m as Record<string, unknown>).jsonrpc === '2.0' &&
    (m as Record<string, unknown>).method === 'initialize'
  );
}

Try / catch

const res = await postToMcp(msg);
if (res.status === 400) {
  const body = await res.json();
  if (body.error?.code === -32000 && /First request must be initialize/.test(body.error.message)) {
    sessionId = await initialize(); // re-run handshake, capture new mcp-session-id
    return postToMcp(msg, sessionId); // retry the original call exactly once
  }
}

Prevention

When it happens

Trigger: POST to /mcp whose body is e.g. {jsonrpc:'2.0',method:'tools/list',id:1} with no mcp-session-id header; a JSON-RPC batch whose elements include no initialize message; a hand-rolled fetch/axios script that skips the handshake; a proxy or gateway that strips the mcp-session-id header, making every request look like a new client.

Common situations: Porting a stdio-based MCP client to streamable HTTP without adding the initialize handshake; forgetting to persist the mcp-session-id response header between requests; sending the notifications/initialized notification before the initialize response arrives; testing the endpoint with curl using a random JSON-RPC method.

Related errors


AI-assisted analysis of abhigyanpatwari/GitNexus@aac7515d2a (2026-08-20). Data as JSON: /api/errors/9d48cd64fa3b2480. Report an issue: GitHub.