github/copilot-sdk · error · InvalidDataException

JSON-RPC frame has a missing, duplicate, or invalid…

Error message

JSON-RPC frame has a missing, duplicate, or invalid Content-Length header.

What it means

ReadMessageAsync validates each header line: if a line starts with the Content-Length prefix but the value was already parsed (duplicate header), fails to parse, or is negative, it throws InvalidDataException. A JSON-RPC frame must carry exactly one valid non-negative integer Content-Length.

Solutions

  1. Fix the peer to emit exactly one header: "Content-Length: <int>\r\n" with an ASCII integer.
  2. Remove duplicate Content-Length headers from any header-building code.
  3. Validate your client's serialization with a known-good JSON-RPC test vector before pointing it at this library.
  4. Check for middlewares that rewrite or append headers.

Example fix

// before
sb.Append($"Content-Length: {len:N0}\r\n"); // locale grouping chars
// after
sb.Append($"Content-Length: {len.ToString(CultureInfo.InvariantCulture)}\r\n");
Defensive patterns

Strategy: validation

Validate before calling

static void ValidateFrameHeader(string header)
{
    var matches = Regex.Matches(header, @"Content-Length:\s*(\d+)");
    if (matches.Count != 1) throw new FormatException("Exactly one valid Content-Length required");
}

Try / catch

try { await reader.ReadLoopAsync(ct); }
catch (InvalidDataException ex) when (ex.Message.Contains("Content-Length header")) {
    logger.LogError(ex, "Peer sent malformed Content-Length; dropping connection");
    transport.Close();
}

Prevention

When it happens

Trigger: Peer sends two Content-Length headers; sends a non-numeric or negative value ("Content-Length: abc"); uses a broken hand-rolled framing writer.

Common situations: Custom protocol implementations that hand-format headers; locale formatting inserting separators; relays/proxies duplicating headers; testing with malformed fixture frames.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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

Appendix: source

Thrown at dotnet/src/JsonRpc.cs:431

        // A missing or unparsable Content-Length means the framing is broken — there's
        // no safe way to resync, so throw and let the read loop terminate the connection.
        int contentLength = -1;
        ReadOnlySpan<byte> prefix = "Content-Length: "u8;
        // headerEnd points just past the \r\n\r\n terminator. Drop only the trailing
        // empty line's \r\n; each remaining header line is still \r\n-terminated and
        // gets split out by the IndexOf below.
        var headerLines = buffer.AsSpan(0, headerEnd - 2);
        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];

View on GitHub (pinned to cd8cf15dc3)