valyala/fasthttp · error · ErrBrokenChunk
cannot unread '\r' char at the end of chunk size: %w
Error message
cannot unread '\r' char at the end of chunk size: %w
What it means
While parsing the end of a chunk-size line, fasthttp read the '\r' byte and then failed to push it back with bufio.Reader.UnreadByte (needed so the following CRLF-reading helper sees it). ErrBrokenChunk wraps the UnreadByte error. This almost always means the bufio reader is in an unexpected state, e.g. a corrupted reader shared between goroutines or a custom bufio implementation.
Source
Thrown at http.go:2985
func parseChunkSize(r *bufio.Reader) (int, error) {
n, err := readHexInt(r)
if err != nil {
return -1, err
}
inExt := false
afterSizeOWS := false
for {
c, err := r.ReadByte()
if err != nil {
return -1, ErrBrokenChunk{
error: fmt.Errorf("cannot read '\\r' char at the end of chunk size: %w", err),
}
}
if c == '\r' {
if err := r.UnreadByte(); err != nil {
return -1, ErrBrokenChunk{
error: fmt.Errorf("cannot unread '\\r' char at the end of chunk size: %w", err),
}
}
break
}
// Security: Don't allow newlines in chunk extensions.
// This can lead to request smuggling issues with some reverse proxies.
if c == '\n' {
return -1, ErrBrokenChunk{
error: errors.New("invalid character '\\n' after chunk size"),
}
}
if inExt {
continue
}
switch c {
case ' ', '\t':
afterSizeOWS = true
continueView on GitHub (pinned to c96f600972)
Solutions
- Ensure the request, response and their bufio readers are used by only one goroutine at a time.
- Use standard bufio.Reader; do not substitute custom reader types in fasthttp internals.
- Handle ErrBrokenChunk with errors.As and close/recycle the connection, since its parse state is unreliable.
- If reproducible with stock fasthttp only, report upstream with a minimal repro.
Example fix
// before (shared across goroutines)
go handle(resp)
go handle(resp) // resp reader shared -> corrupted state
// after
for resp := range responses {
go handle(resp) // one reader per goroutine
} Defensive patterns
Strategy: type-guard
Type guard
func isUnreadBrokenChunk(err error) bool {
var bc fasthttp.ErrBrokenChunk
if !errors.As(err, &bc) { return false }
return strings.Contains(bc.error.Error(), "cannot unread")
} Try / catch
var bc fasthttp.ErrBrokenChunk
if errors.As(err, &bc) && strings.Contains(bc.error.Error(), "cannot unread") {
// bufio reader state corrupted: do not reuse the reader/conn
_ = conn.Close()
} Prevention
- Never share fasthttp Request/Response or bufio.Reader across goroutines.
- Use only standard bufio.Reader with fasthttp internals.
- Recycle request/response objects strictly one-at-a-time via Acquire/Release.
- Close the connection after this error; parse state is unreliable.
When it happens
Trigger: Parsing a chunked body when ReadByte succeeds returning '\r' but UnreadByte fails — only possible with a non-standard bufio.Reader implementation or a reader corrupted by concurrent use.
Common situations: Sharing a fasthttp Request/Response or its bufio.Reader across goroutines; passing a custom bufio-like reader into low-level fasthttp APIs.
Related errors
- bufio read returned (%d, nil)
- cannot read '\r' char at the end of chunk size: %w
- invalid character %q after chunk size
- cannot read %q char at the end of chunk size: %w
- unexpected char %q at the end of chunk size: expected %q
AI-assisted analysis of valyala/fasthttp@c96f600972 (2026-08-31).
Data as JSON: /api/errors/77ca6f37edac2096.
Report an issue: GitHub.