Example: io.LimitReader
io.LimitReader wraps an io.Reader and limits how many bytes can be read from it.
Use it when downstream code should see at most N bytes without buffering the entire input.
The important detail is:
EOF from a limited Reader does not necessarily mean EOF from the underlying Reader.
Quick Example
Output:
The limited reader exposes only five bytes. When those five bytes have been consumed, it returns io.EOF even though the underlying reader still contains data.
Detect Oversized Input
A common use is protecting io.ReadAll from unexpectedly large input.
This limits how much can be consumed:
But it does not tell you whether the original input exceeded maxSize.
For input validation, read one extra byte:
The extra byte is included in data. If the input is oversized, reject it rather than parsing the truncated data.
If the application intentionally accepts only the first maxSize bytes, truncate explicitly with data[:maxSize] instead.
Common Use
Limit io.ReadAll
This prevents io.ReadAll from consuming more than maxSize bytes through the limited reader.
It is useful when the application only needs a maximum amount of data and does not need to distinguish:
- input ended at
maxSize - input continued beyond
maxSize
If exceeding the limit is an error, use the maxSize + 1 pattern instead.
Protocol Boundaries
io.LimitReader is useful for exposing a maximum-sized region to downstream code:
But a maximum boundary is different from an exact-length requirement.
Use:
io.LimitReader→ at most N bytesio.ReadFull→ exactly N bytes
Common Mistakes
1. Treating limit-induced EOF as underlying EOF
A successful read ending at 1024 bytes does not prove that r has reached EOF.
The underlying reader may still contain more data.
2. Assuming io.LimitReader rejects oversized input
It does not.
silently stops at the limit.
If oversized input must be rejected, use maxSize + 1 and check the resulting length.
3. Assuming it closes the underlying reader
io.LimitReader does not close r.
If r also implements io.Closer, its lifecycle remains the caller's responsibility.
4. Expecting timeouts or cancellation
io.LimitReader limits bytes, not time.
It does not:
- set deadlines
- cancel a blocked read
- close the underlying connection
- enforce a request timeout
Use the appropriate context, deadline, or connection-level mechanism when those guarantees are required.
HTTP Request Body Limits
For HTTP server request bodies, use http.MaxBytesReader rather than building the limit yourself with io.LimitReader:
When the limit is exceeded, the reader returns a *http.MaxBytesError.
The handler is still responsible for deciding how to respond, such as returning HTTP 413.
API Selection Guide
Rule of Thumb
Use
io.LimitReaderto limit how much a reader exposes. UseN+1when you also need to detect oversized input.