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 = t

View on GitHub (pinned to c96f600972)

Solutions

  1. Fix the sender to terminate chunk-size lines with CRLF, not bare LF.
  2. Check front-end proxies/load balancers for CRLF-normalization and disable it for chunked bodies.
  3. Treat the peer as misbehaving: fasthttp returns ErrBrokenChunk; close the connection and log for security review.
  4. 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

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


AI-assisted analysis of valyala/fasthttp@c96f600972 (2026-08-31). Data as JSON: /api/errors/847b9457e6cd352d. Report an issue: GitHub.