XINCGer/Unity3DTraining · error · InvalidOperationException
Stream.Read returned a negative count
Error message
Stream.Read returned a negative count
What it means
When refilling its internal buffer, CodedInputStream calls input.Read(...) and requires a non-negative count, as the Stream contract mandates. A negative return means the underlying Stream implementation is buggy or misbehaving, so InvalidOperationException is thrown. This surfaces a problem in the custom stream, not in the protobuf data itself.
Solutions
- Fix the custom Stream.Read implementation to return 0 at end of stream and to throw exceptions on errors instead of returning negative values.
- Map native error codes to exceptions (throw IOException) rather than returning them from Read.
- If a third-party stream is at fault, wrap it in an adapter Stream that sanitizes negative returns.
- Test the stream with a contract check: Read must return 0..buffer.Length and only 0 at EOF.
Example fix
// before
public override int Read(byte[] buffer, int offset, int count)
{
int n = NativeRecv(...);
return n; // may be -1 on error
}
// after
public override int Read(byte[] buffer, int offset, int count)
{
int n = NativeRecv(...);
if (n < 0) throw new IOException("recv failed: " + n);
return n;
} Defensive patterns
Strategy: type-guard
Validate before calling
// contract check for custom streams before use
var tmp = new byte[1];
int probe = stream.Read(tmp, 0, 1);
if (probe < 0) throw new IOException("Stream.Read violates contract (negative return)"); Type guard
bool ConformsToStreamContract(Stream s) { try { var b = new byte[1]; return s.Read(b, 0, 1) >= 0; } catch { return false; } } Try / catch
try { message.MergeFrom(codedInput); } catch (InvalidOperationException ex) when (ex.Message.Contains("negative count")) { logger.LogError(ex, "Wrapped Stream returned negative from Read — fix the stream implementation"); } Prevention
- Custom Stream.Read must return 0 at EOF and throw IOException on errors, never a negative number.
- Unit-test custom streams against the Stream contract before wiring them into parsers.
- Wrap unmanaged/native reads in an adapter that converts error codes to exceptions.
When it happens
Trigger: Wrapping CodedInputStream around a custom Stream subclass whose Read override returns -1 (common mistake: returning -1 instead of 0 at end of stream) or a negative value from an unchecked native call.
Common situations: Network stream wrappers where recv() error codes (-1) are propagated as the Read result; Unity/games with hand-written socket streams; wrappers around unmanaged decoders returning error codes from Read.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Offset must be within the buffer
- Length must be non-negative and within the buffer
- SpaceLeft can only be called on CodedOutputStreams that are…
- Key already exists in map
- Key is null
AI-assisted analysis of XINCGer/Unity3DTraining@016f98412e (2026-09-12).
Data as JSON: /api/errors/69fc9bb345cc9b4e.
Report an issue: GitHub.
Appendix: source
Thrown at NetWorkAndResources/Socket_Protobuff/Assets/Plugins/Google.Protobuf/CodedInputStream.cs:1032
{
// Oops, we hit a limit.
if (mustSucceed)
{
throw InvalidProtocolBufferException.TruncatedMessage();
}
else
{
return false;
}
}
totalBytesRetired += bufferSize;
bufferPos = 0;
bufferSize = (input == null) ? 0 : input.Read(buffer, 0, buffer.Length);
if (bufferSize < 0)
{
throw new InvalidOperationException("Stream.Read returned a negative count");
}
if (bufferSize == 0)
{
if (mustSucceed)
{
throw InvalidProtocolBufferException.TruncatedMessage();
}
else
{
return false;
}
}
else
{
RecomputeBufferSizeAfterLimit();
int totalBytesRead =
totalBytesRetired + bufferSize + bufferSizeAfterLimit;
if (totalBytesRead > sizeLimit || totalBytesRead < 0)View on GitHub (pinned to 016f98412e)