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

  1. Inspect the wrapped cause via errors.As on ErrBrokenChunk; treat it as a connection-level failure, not a data-format issue.
  2. Add client-side retry for idempotent requests and resumable uploads to survive disconnects.
  3. Adjust proxy/load-balancer timeouts so long chunked transfers aren't cut off.
  4. 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

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


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