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
- Fix the peer to emit exactly one header: "Content-Length: <int>\r\n" with an ASCII integer.
- Remove duplicate Content-Length headers from any header-building code.
- Validate your client's serialization with a known-good JSON-RPC test vector before pointing it at this library.
- 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
- Format header ints with CultureInfo.InvariantCulture
- Build headers with a shared, tested framing helper
- Never duplicate Content-Length in header builders
- Test senders against reference JSON-RPC fixtures
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
- JSON-RPC frame is missing the Content-Length header.
- Stream ended while reading JSON-RPC headers.
- Client not started. Call start() first.
- Expected JSON object for `params` of single-object-param…
- Unexpected end of stream while reading JSON-RPC message
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)