{"record":{"id":"0718a5333bc0b6d9","repo":"biomejs/biome","slug":"could-not-find-colon-token-in-line","errorCode":null,"errorMessage":"could not find colon token in \"${line}\"","messagePattern":"could not find colon token in \"(.+?)\"","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/@biomejs/backend-jsonrpc/src/transport.ts","lineNumber":228,"sourceCode":"\t\tif (line === \"\\r\\n\") {\n\t\t\tconst { contentLength, contentType } = readerState;\n\t\t\tif (typeof contentLength !== \"number\") {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"incoming message from the remote workspace is missing the Content-Length header\",\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tthis.readerState = {\n\t\t\t\tkind: ReaderStateKind.Body,\n\t\t\t\tcontentLength,\n\t\t\t\tcontentType,\n\t\t\t};\n\t\t\treturn;\n\t\t}\n\n\t\tconst colonIndex = line.indexOf(\":\");\n\t\tif (colonIndex < 0) {\n\t\t\tthrow new Error(`could not find colon token in \"${line}\"`);\n\t\t}\n\n\t\tconst headerName = line.substring(0, colonIndex);\n\t\tconst headerValue = line.substring(colonIndex + 1).trim();\n\n\t\tswitch (headerName) {\n\t\t\tcase \"Content-Length\": {\n\t\t\t\tconst value = Number.parseInt(headerValue, 10);\n\t\t\t\treaderState.contentLength = value;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"Content-Type\": {\n\t\t\t\tif (!headerValue.startsWith(MIME_JSONRPC)) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`invalid value for Content-Type expected \"${MIME_JSONRPC}\", got \"${headerValue}\"`,\n\t\t\t\t\t);\n\t\t\t\t}\n","sourceCodeStart":210,"sourceCodeEnd":246,"githubUrl":"https://github.com/biomejs/biome/blob/7529811358079edb5a2a9d4a5f67a9f639a63f3a/packages/@biomejs/backend-jsonrpc/src/transport.ts#L210-L246","documentation":"While parsing a header block, every non-blank line must be a 'Name: Value' pair; a line with no colon character cannot be a header and throws (transport.ts:226-229). Note the blank-line check is an exact match against \"\\r\\n\" (transport.ts:210), so a peer that terminates the header block with a bare \\n (LF only) produces the line \"\\n\", which has no colon and hits this error. It also fires when the parser is desynchronized and is reading body or garbage bytes as header lines.","triggerScenarios":"The peer uses LF-only line endings in headers; a previous message declared a wrong Content-Length so the reader consumed too few/many bytes and now parses body JSON as headers; or any non-protocol bytes appear where a header line is expected.","commonSituations":"Custom test harnesses or mock servers writing headers with \\n instead of \\r\\n; hand-rolled framing code that computes Content-Length from string length instead of byte length, desynchronizing the stream on multi-byte UTF-8 content.","solutions":["Make the sender use CRLF (\\r\\n) line endings for every header line including the blank terminator, matching sendMessage (transport.ts:167).","Check the preceding message's Content-Length: it must equal the exact byte length of the JSON body (use Buffer.byteLength / body.length of a Buffer, not string .length).","Log raw chunks around the failure to find where framing desynchronized; the offending line is embedded in the error message.","Confirm you are talking to a process that actually implements this framing (the Biome daemon), not a plain JSON stream."],"exampleFix":"// before: LF-only header block (line \"\\n\" has no colon -> throws)\nsocket.write(`Content-Length: ${body.length}\\n\\n`);\nsocket.write(body);\n\n// after: CRLF header block\nsocket.write(`Content-Length: ${body.length}\\r\\nContent-Type: application/vscode-jsonrpc;charset=utf-8\\r\\n\\r\\n`);\nsocket.write(body);","handlingStrategy":"validation","validationCode":"// Validate outgoing frames on the peer side before writing them\nfunction frame(message) {\n\tconst body = Buffer.from(JSON.stringify(message)); // byte length, not string length\n\tconst headers = `Content-Length: ${body.length}\\r\\nContent-Type: application/vscode-jsonrpc;charset=utf-8\\r\\n\\r\\n`;\n\tif (!headers.endsWith(\"\\r\\n\\r\\n\")) throw new Error(\"header block must end with CRLF CRLF\");\n\treturn Buffer.concat([Buffer.from(headers), body]);\n}","typeGuard":null,"tryCatchPattern":"// Header parsing runs in the 'data' event; treat any framing error as fatal for the connection:\nprocess.on(\"uncaughtException\", (err) => {\n\tif (err.message.includes(\"could not find colon token\")) {\n\t\t// framing is desynchronized; recovery by re-reading is not possible\n\t\ttransport.destroy();\n\t\treconnect();\n\t} else {\n\t\tthrow err;\n\t}\n});","preventionTips":["Always emit CRLF line endings in header blocks; a bare \\n blank line produces the colon-less line \"\\n\" that triggers this error.","Compute Content-Length from Buffer.byteLength(body) so multi-byte UTF-8 never desynchronizes framing.","When mocking the daemon in tests, reuse a known-good framing helper rather than hand-writing headers."],"tags":["jsonrpc","transport","protocol","headers","framing"],"backgroundTag":"jsonrpc-header-framing","analyzedSha":"7529811358079edb5a2a9d4a5f67a9f639a63f3a","analyzedAt":"2026-08-16T22:13:27.842Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}