valyala/fasthttp · error · ErrBrokenChunk
unexpected char %q at the end of chunk size: expected %q
Error message
unexpected char %q at the end of chunk size: expected %q
What it means
In readCrLf, after the chunk size (and optional extension), fasthttp expects exactly '\r' then '\n'. If the byte read differs from the expected one it returns ErrBrokenChunk with this message, hardening against malformed chunked framing used in request-smuggling attacks.
Source
Thrown at http.go:3035
}
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)
//
// is equivalent to
//
// c.DoTimeout(&req, &resp, t)
func (req *Request) SetTimeout(t time.Duration) {
req.timeout = tView on GitHub (pinned to c96f600972)
Solutions
- Fix the sender to terminate chunk-size lines with CRLF, not bare LF.
- Check front-end proxies/load balancers for CRLF-normalization and disable it for chunked bodies.
- Treat the peer as misbehaving: fasthttp returns ErrBrokenChunk; close the connection and log for security review.
- If you control the client library, use an HTTP-conforming serializer instead of hand-built chunked output.
Example fix
// before (wire format) 5\nhello\n0\n\n // after 5\r\nhello\r\n0\r\n\r\n
Defensive patterns
Strategy: type-guard
Validate before calling
// Client side, verify every chunk-size line ends with CRLF before sending:
func validChunkLineEnd(line []byte) bool {
return bytes.HasSuffix(line, []byte("\r\n"))
} Type guard
func isBadCRLF(err error) bool {
var bc fasthttp.ErrBrokenChunk
return errors.As(err, &bc) &&
strings.Contains(bc.error.Error(), "unexpected char")
} Try / catch
var bc fasthttp.ErrBrokenChunk
if errors.As(err, &bc) && strings.Contains(bc.error.Error(), "unexpected char") {
// peer used bare LF or garbage: close, log source for security review
ctx.ConnectionClose()
} Prevention
- Always terminate chunk lines with \r\n, never bare \n.
- Disable CRLF-to-LF normalization in edge proxies for chunked bodies.
- Flag repeat offenders: this pattern is common in smuggling probes.
- Use conforming HTTP client libraries rather than hand-built chunk encoding.
When it happens
Trigger: Peer sends something other than CRLF to end the chunk-size line — e.g. bare LF ("5\n"), a stray space, or binary noise at that position.
Common situations: HTTP/1.0-style or hand-rolled clients using bare LF line endings; lenient proxies normalizing CRLF to LF; fuzzing/scanning traffic; desync between a front proxy and fasthttp.
Related errors
- invalid character %q after chunk size
- cannot read '\r' char at the end of chunk size: %w
- empty hex number
- invalid character '\n' after chunk size
- cannot unread '\r' char at the end of chunk size: %w
AI-assisted analysis of valyala/fasthttp@c96f600972 (2026-08-31).
Data as JSON: /api/errors/847b9457e6cd352d.
Report an issue: GitHub.