biomejs/biome · error · Error

incoming message from the remote workspace is missing the Co

Error message

incoming message from the remote workspace is missing the Content-Length header

What it means

The Transport class in @biomejs/backend-jsonrpc speaks the LSP-style JSON-RPC wire protocol over a socket: each message is preceded by a header block terminated by a blank \r\n line, and Content-Length tells the reader how many body bytes follow (transport.ts:209-224). When the blank line arrives but the reader state never recorded a Content-Length value (typeof contentLength !== "number"), the framer has no way to know the body size and throws this error. In practice it means the bytes on the socket are not framed the way this Transport expects.

Source

Thrown at packages/@biomejs/backend-jsonrpc/src/transport.ts:213

				this.pendingData = this.pendingData.subarray(
					this.readerState.contentLength,
				);
				this.processIncomingBody(body);

				this.readerState = {
					kind: ReaderStateKind.Header,
				};
			} else {
				break;
			}
		}
	}

	private processIncomingHeader(readerState: ReaderStateHeader, line: string) {
		if (line === "\r\n") {
			const { contentLength, contentType } = readerState;
			if (typeof contentLength !== "number") {
				throw new Error(
					"incoming message from the remote workspace is missing the Content-Length header",
				);
			}

			this.readerState = {
				kind: ReaderStateKind.Body,
				contentLength,
				contentType,
			};
			return;
		}

		const colonIndex = line.indexOf(":");
		if (colonIndex < 0) {
			throw new Error(`could not find colon token in "${line}"`);
		}

		const headerName = line.substring(0, colonIndex);

View on GitHub (pinned to 7529811358)

Solutions

  1. Verify the socket is connected to the actual Biome daemon (correct host/port/socket path), not to an HTTP endpoint or other service.
  2. Log raw chunks on the socket and confirm every message is framed as 'Content-Length: <n>\r\nContent-Type: application/vscode-jsonrpc;charset=utf-8\r\n\r\n<json body>' exactly like sendMessage does (transport.ts:164-170).
  3. Align versions: use a @biomejs/backend-jsonrpc (or @biomejs/js-api / daemon client) whose protocol matches the running daemon.
  4. If you implement the peer yourself, always write a Content-Length header whose value is the byte length of the body before writing the body.

Example fix

// before: peer writes the JSON body with no headers
socket.write(Buffer.from(JSON.stringify(message)));

// after: peer frames the message like Transport.sendMessage does
const body = Buffer.from(JSON.stringify(message));
const headers = Buffer.from(
	`Content-Length: ${body.length}\r\nContent-Type: application/vscode-jsonrpc;charset=utf-8\r\n\r\n`,
);
socket.write(Buffer.concat([headers, body]));
Defensive patterns

Strategy: validation

Validate before calling

// Peek at the first chunk BEFORE constructing Transport, to verify the peer speaks the header framing
function assertPeerFraming(socket) {
	return new Promise((resolve, reject) => {
		socket.once("data", (chunk) => {
			const head = chunk.toString("utf-8", 0, 128);
			if (/^Content-Length:\s*\d+/i.test(head)) {
				resolve(socket);
			} else {
				socket.destroy();
				reject(new Error(`peer does not speak the JSON-RPC framing protocol, got: ${JSON.stringify(head)}`));
			}
		});
	});
}

Try / catch

// The throw happens inside the socket 'data' handler, so it surfaces as an uncaught exception.
// Catch it there and fail the connection instead of the process:
process.on("uncaughtException", (err) => {
	if (err.message.includes("missing the Content-Length header")) {
		transport.destroy();
		reconnect();
	} else {
		throw err;
	}
});

Prevention

When it happens

Trigger: The peer writes a header block ending in \r\n without a Content-Length header; the socket is connected to a process that is not the Biome daemon (HTTP server, raw JSON stream, REPL banner); or an earlier framing desync causes arbitrary bytes to be interpreted as a header block. Triggered from processIncomingHeader, reached via the socket 'data' handler registered in the Transport constructor (transport.ts:117-119).

Common situations: Pointing the Transport at the wrong port or socket path; a version mismatch where the daemon's wire format changed; a proxy or wrapper injecting bytes into the stream; a custom mock daemon in tests that writes JSON bodies without headers.

Related errors


AI-assisted analysis of biomejs/biome@7529811358 (2026-08-16). Data as JSON: /api/errors/c845022926f3ddc5. Report an issue: GitHub.