github/copilot-sdk · error · InvalidDataException

JSON-RPC frame is missing the Content-Length header.

Error message

JSON-RPC frame is missing the Content-Length header.

What it means

After scanning all header lines, if no Content-Length header was seen (contentLength still -1), ReadMessageAsync throws InvalidDataException: the frame simply omitted the mandatory Content-Length header, so the body size is unknowable.

Solutions

  1. Always prefix each message with "Content-Length: <byteCount>\r\n\r\n" before the JSON body.
  2. Compute Content-Length from the UTF-8 byte length of the payload, not the character count.
  3. If you need newline-delimited JSON, use a transport mode that supports it instead of the framed reader.
  4. Test the sender against a header-framed reference implementation.

Example fix

// before
await stream.WriteAsync(JsonBytes); // no header
// after
var json = Encoding.UTF8.GetBytes(payload);
var header = Encoding.ASCII.GetBytes($"Content-Length: {json.Length}\r\n\r\n");
await stream.WriteAsync(header); await stream.WriteAsync(json);
Defensive patterns

Strategy: validation

Validate before calling

static byte[] Frame(byte[] json)
{
    var header = Encoding.ASCII.GetBytes($"Content-Length: {json.Length}\r\n\r\n");
    var frame = new byte[header.Length + json.Length];
    header.CopyTo(frame, 0); json.CopyTo(frame, header.Length);
    return frame;
} // use Frame() for every outgoing message

Try / catch

try { await reader.ReadLoopAsync(ct); }
catch (InvalidDataException ex) when (ex.Message.Contains("missing the Content-Length")) {
    logger.LogError(ex, "Peer omitted Content-Length; not a framed JSON-RPC peer");
    transport.Close();
}

Prevention

When it happens

Trigger: Peer writes only e.g. "Content-Type: ..." or a bare "\r\n" delimiter with no Content-Length line; newline-only ping frames; a sender that forgot to include the header entirely.

Common situations: Implementations of the LSP/JSON-RPC wire protocol that assume newline-delimited JSON instead of header-framed messages; sending raw JSON without the Content-Length wrapper.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09). Data as JSON: /api/errors/02a383c0f04dbb89. Report an issue: GitHub.

Appendix: source

Thrown at dotnet/src/JsonRpc.cs:439

        while (!headerLines.IsEmpty)
        {
            int lineEnd = headerLines.IndexOf("\r\n"u8);
            ReadOnlySpan<byte> line = lineEnd >= 0 ? headerLines.Slice(0, lineEnd) : headerLines;

            if (line.StartsWith(prefix) &&
                (contentLength >= 0 ||
                 !int.TryParse(line.Slice(prefix.Length), NumberStyles.None, CultureInfo.InvariantCulture, out contentLength) ||
                 contentLength < 0))
            {
                throw new InvalidDataException("JSON-RPC frame has a missing, duplicate, or invalid Content-Length header.");
            }

            headerLines = lineEnd >= 0 ? headerLines.Slice(lineEnd + 2) : default;
        }

        if (contentLength < 0)
        {
            throw new InvalidDataException("JSON-RPC frame is missing the Content-Length header.");
        }

        // Bytes after the header that we already have
        int extraBytes = filled - headerEnd;

        // Ensure buffer is large enough for the body and any overflow already read.
        int needed = Math.Max(contentLength, extraBytes);
        if (needed > buffer.Length)
        {
            var newBuffer = new byte[needed];
            Buffer.BlockCopy(buffer, headerEnd, newBuffer, 0, extraBytes);
            buffer = newBuffer;
        }
        else if (extraBytes > 0)
        {
            Buffer.BlockCopy(buffer, headerEnd, buffer, 0, extraBytes);
        }

View on GitHub (pinned to cd8cf15dc3)