valyala/fasthttp · error · ErrBrokenChunk
cannot read %q char at the end of chunk size: %w
Error message
cannot read %q char at the end of chunk size: %w
What it means
readCrLf verifies the CRLF that terminates a chunk-size line byte by byte. This ErrBrokenChunk is returned when the read of the expected byte ('\r' or '\n') fails at the I/O level — the connection was closed, reset, or timed out before the chunk line's terminator arrived.
Source
Thrown at http.go:3030
default:
return -1, ErrBrokenChunk{
error: fmt.Errorf("invalid character %q after chunk size", c),
}
}
}
err = readCrLf(r)
if err != nil {
return -1, err
}
return n, nil
}
func readCrLf(r *bufio.Reader) error {
for _, exp := range []byte{'\r', '\n'} {
c, err := r.ReadByte()
if err != nil {
return ErrBrokenChunk{
error: fmt.Errorf("cannot read %q char at the end of chunk size: %w", exp, err),
}
}
if c != exp {
return ErrBrokenChunk{
error: fmt.Errorf("unexpected char %q at the end of chunk size: expected %q", c, exp),
}
}
}
return nil
}
// SetTimeout sets timeout for the request.
//
// The following code:
//
// req.SetTimeout(t)
// c.Do(&req, &resp)
//View on GitHub (pinned to c96f600972)
Solutions
- Inspect the wrapped cause via errors.As on ErrBrokenChunk; treat it as a connection-level failure, not a data-format issue.
- Add client-side retry for idempotent requests and resumable uploads to survive disconnects.
- Adjust proxy/load-balancer timeouts so long chunked transfers aren't cut off.
- Verify the sender terminates every chunk line with CRLF before closing the connection.
Example fix
// before
if err := client.Do(req, resp); err != nil { log.Print(err) }
// after
if err := client.Do(req, resp); err != nil {
var bc fasthttp.ErrBrokenChunk
if errors.As(err, &bc) {
log.Printf("peer sent broken chunked body: %v", bc.error)
}
return retryOrFail(err) // retry idempotent ops
} Defensive patterns
Strategy: retry
Type guard
func isChunkReadFailure(err error) bool {
var bc fasthttp.ErrBrokenChunk
if !errors.As(err, &bc) { return false }
return errors.Is(bc.error, io.EOF) || errors.Is(bc.error, io.ErrUnexpectedEOF) || errors.Is(bc.error, syscall.ECONNRESET)
} Try / catch
err := client.DoTimeout(req, resp, timeout)
var bc fasthttp.ErrBrokenChunk
if errors.As(err, &bc) {
if isRetryable(req) {
return retry(req, resp) // idempotent request: retry once
}
return err
} Prevention
- Retry idempotent requests when chunked reads fail at the I/O level.
- Tune proxy/LB idle and response timeouts above the slowest expected transfers.
- Use resumable uploads for large chunked bodies from unreliable clients.
- Alert on spikes: frequent occurrences indicate disconnecting clients or truncating proxies.
When it happens
Trigger: Peer disconnects or the connection errors while fasthttp reads the CRLF after a chunk size (or between chunks); proxies truncating the stream; read timeout firing mid-line.
Common situations: Mobile clients dropping mid-upload; upstream servers killed mid-response; aggressive idle-timeout proxies; network failures during large chunked transfers.
Related errors
- cannot read '\r' char at the end of chunk size: %w
- cannot unread '\r' char at the end of chunk size: %w
- invalid character %q after chunk size
- unexpected char %q at the end of chunk size: expected %q
- fasthttp: no args value for the given key
AI-assisted analysis of valyala/fasthttp@c96f600972 (2026-08-31).
Data as JSON: /api/errors/7117083e3d4f4cce.
Report an issue: GitHub.