• English
  • 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

    package main
    
    import (
    	"fmt"
    	"io"
    	"strings"
    )
    
    func main() {
    	src := strings.NewReader("Hello, io.LimitReader from gobase.net.")
    
    	limited := io.LimitReader(src, 5)
    
    	data, err := io.ReadAll(limited)
    	if err != nil {
    		fmt.Println("read failed:", err)
    		return
    	}
    
    	fmt.Printf("%s\n", data)
    	fmt.Printf("Remaining bytes: %d\n", src.Len())
    }

    Output:

    Hello
    Remaining bytes: 33

    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:

    data, err := io.ReadAll(io.LimitReader(r, maxSize))
    if err != nil {
    	return err
    }

    But it does not tell you whether the original input exceeded maxSize.

    For input validation, read one extra byte:

    var errTooLarge = errors.New("input exceeds maximum size")
    
    data, err := io.ReadAll(io.LimitReader(r, maxSize+1))
    if err != nil {
    	return err
    }
    
    if int64(len(data)) > maxSize {
    	return errTooLarge
    }

    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

    data, err := io.ReadAll(io.LimitReader(r, maxSize))

    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:

    field := io.LimitReader(r, 1024)

    But a maximum boundary is different from an exact-length requirement.

    Use:

    • io.LimitReaderat most N bytes
    • io.ReadFullexactly N bytes

    Common Mistakes

    1. Treating limit-induced EOF as underlying EOF

    limited := io.LimitReader(r, 1024)
    
    data, err := io.ReadAll(limited)

    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.

    io.ReadAll(io.LimitReader(r, maxSize))

    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:

    r.Body = http.MaxBytesReader(w, r.Body, maxSize)

    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

    NeedRecommended MechanismBehavior
    Cap a read at N bytesio.LimitReader(r, N)Stops at N bytes and returns io.EOF
    Detect and reject input larger than N bytesio.LimitReader(r, N+1)Read one extra byte, then reject when len(data) > N
    Limit an HTTP request bodyhttp.MaxBytesReader(w, r, N)Returns *http.MaxBytesError when the limit is exceeded
    Read exactly N bytesio.ReadFull(r, buf)Returns io.ErrUnexpectedEOF if fewer than N bytes are available
    Read a bounded region from a ReaderAtio.SectionReader(r, off, n)Exposes a fixed offset-based read window

    Rule of Thumb

    Use io.LimitReader to limit how much a reader exposes. Use N+1 when you also need to detect oversized input.