Skip to content

transport: return unexpected EOF for partial message headers #9200

Description

@buger

Summary

Stream.ReadMessageHeader is intended to return io.ErrUnexpectedEOF when EOF occurs after only part of the 5-byte gRPC message header has been read. When the partial bytes and EOF arrive from separate recv buffer messages, the final read can return n == 0, io.EOF, so the method returns clean EOF even though earlier iterations already read part of the header.

Why this matters

The error returned by ReadMessageHeader tells callers whether the stream ended cleanly or whether a gRPC message header was truncated. Returning io.EOF after partial header progress hides the fact that malformed or truncated message data was observed.

This makes truncated message-header handling less precise and can make debugging or higher-level error handling harder.

Environment

gRPC version: current master at d74861eb

Go version: go version go1.25.1 X:nodwarf5 linux/amd64

OS: Linux claude 6.16.5-arch1-1 #1 SMP PREEMPT_DYNAMIC Thu, 04 Sep 2025 23:18:13 +0000 x86_64 GNU/Linux

Trigger

Read a gRPC message header where some bytes of the 5-byte header arrive first, and EOF arrives later as a separate recv buffer message.

Expected behavior

Any EOF after partial header progress should be reported as io.ErrUnexpectedEOF.

Actual behavior

Partial header data followed by EOF can be reported as io.EOF.

Proposed fix

PR #9204 tracks cumulative bytes read while filling a gRPC message header. If EOF occurs after any partial progress, it returns io.ErrUnexpectedEOF even when the final read call itself returned zero bytes. The PR includes a regression test that splits a partial header and EOF across separate recv buffer messages.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions