abhigyanpatwari/GitNexus · error · Error

Missing Content-Length header from MCP client

Error message

Missing Content-Length header from MCP client

What it means

The compatible stdio transport reads LSP-style framed JSON-RPC: a header block terminated by a blank line followed by a Content-Length-delimited body. After locating the header terminator, it regex-matches for a content-length header; when no match exists it discards all buffered input and throws, because the stream is not speaking the expected framing protocol.

Source

Thrown at gitnexus/src/mcp/compatible-stdio-transport.ts:146

  private readContentLengthMessage(): JSONRPCMessage | null {
    if (!this._readBuffer) {
      return null;
    }

    const header = findHeaderEnd(this._readBuffer);
    if (header === null) {
      return null;
    }

    const headerText = this._readBuffer
      .toString('utf8', 0, header.index)
      .replace(/\r\n/g, '\n')
      .replace(/\r/g, '\n');
    const match = headerText.match(/(?:^|\n)content-length\s*:\s*(\d+)/i);
    if (!match) {
      this.discardBufferedInput();
      throw new Error('Missing Content-Length header from MCP client');
    }

    const contentLength = Number.parseInt(match[1], 10);
    if (!Number.isFinite(contentLength) || contentLength < 0) {
      this.discardBufferedInput();
      throw new Error('Invalid Content-Length header from MCP client');
    }
    if (contentLength > MAX_BUFFER_SIZE) {
      this.discardBufferedInput();
      throw new Error(
        `Content-Length ${contentLength} exceeds maximum allowed size (${MAX_BUFFER_SIZE} bytes)`,
      );
    }
    const bodyStart = header.index + header.separatorLength;
    const bodyEnd = bodyStart + contentLength;
    if (this._readBuffer.length < bodyEnd) {
      return null;
    }

View on GitHub (pinned to aac7515d2a)

Solutions

  1. Use the official MCP SDK client (TypeScript/Python), which performs Content-Length framing correctly.
  2. If hand-rolling, write `Content-Length: <utf8 byte length>\r\n\r\n` before each JSON body — measure bytes, not string length.
  3. Remove any stray console.log/print to stdout in the client; route client logs to stderr.
  4. Restart the MCP session after this throw: discardBufferedInput() has already dropped pending bytes, so the stream state is unrecoverable.

Example fix

// before: unframed NDJSON (throws Missing Content-Length header)
process.stdout.write(JSON.stringify(rpcRequest) + '\n');

// after: LSP-style framing (what the SDK does)
const body = Buffer.from(JSON.stringify(rpcRequest), 'utf8');
process.stdout.write(`Content-Length: ${body.length}\r\n\r\n`);
process.stdout.write(body);
Defensive patterns

Strategy: validation

Validate before calling

// Client-side: only write properly framed messages, never raw lines
import * as net from 'node:net';

function writeFrame(sock: net.Socket, msg: unknown): void {
  const body = Buffer.from(JSON.stringify(msg), 'utf8');
  if (body.length === 0) throw new Error('refusing to send empty frame');
  sock.write(`Content-Length: ${body.length}\r\n\r\n`);
  sock.write(body);
}

Try / catch

// Server-side: a framing error means the byte stream is unrecoverable (input was discarded)
try {
  transport.processReadData();
} catch (err) {
  if (err instanceof Error && err.message.startsWith('Missing Content-Length')) {
    log.error('client speaks unframed stdio — closing session', { err });
    await transport.close(); // do NOT reuse the session
    return;
  }
  throw err;
}

Prevention

When it happens

Trigger: An MCP client writes raw newline-delimited JSON to GitNexus's stdin instead of Content-Length framed messages; a hand-rolled client writes plain JSON.stringify(msg) + '\n'; a client accidentally interleaves log/debug output into its stdout (GitNexus's stdin) ahead of a real message; a proxy or wrapper mangles the byte stream.

Common situations: Custom MCP clients not built on the official SDK; confusion between NDJSON-based transports and the LSP framing the SDK uses; debug prints accidentally left in a client's stdout path; integrating GitNexus MCP behind a homemade pipe/spawn wrapper that buffers or rewrites frames.

Related errors


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