valyala/fasthttp · error

fasthttp: unexpected char at the end of status code

Error message

fasthttp: unexpected char at the end of status code

What it means

While parsing a response status line, fasthttp reads the 3-digit status code and expects it to be followed by a space, CR/LF, or end of line. If an unexpected character appears where the status code should end, ErrUnexpectedStatusCodeChar is returned. It indicates the peer sent a syntactically invalid status line.

Source

Thrown at header.go:473

// The following trailers are forbidden:
// 1. necessary for message framing (e.g., Transfer-Encoding and Content-Length),
// 2. routing (e.g., Host),
// 3. request modifiers (e.g., controls and conditionals in Section 5 of [RFC7231]),
// 4. authentication (e.g., see [RFC7235] and [RFC6265]),
// 5. response control data (e.g., see Section 7.1 of [RFC7231]),
// 6. determining how to process the payload (e.g., Content-Encoding, Content-Type, Content-Range, and Trailer)
//
// Return ErrBadTrailer if contain any forbidden trailers.
func (h *header) AddTrailer(trailer string) error {
	return h.AddTrailerBytes(s2b(trailer))
}

var (
	ErrBadTrailer                    = errors.New("fasthttp: contain forbidden trailer")
	ErrReadingResponseHeaders        = errors.New("fasthttp: error when reading response headers")
	ErrReadingResponseTrailer        = errors.New("fasthttp: error when reading response trailer")
	ErrResponseFirstLineMissingSpace = errors.New("fasthttp: cannot find whitespace in the first line of response")
	ErrUnexpectedStatusCodeChar      = errors.New("fasthttp: unexpected char at the end of status code")
	ErrMissingRequestMethod          = errors.New("fasthttp: cannot find http request method")
	ErrUnsupportedRequestMethod      = errors.New("fasthttp: unsupported http request method")
	ErrExtraWhitespaceInRequestLine  = errors.New("fasthttp: extra whitespace in request line")
	ErrEmptyRequestURI               = errors.New("fasthttp: requesturi cannot be empty")
	ErrDuplicateContentLength        = errors.New("fasthttp: duplicate content-length header")
	ErrUnsupportedTransferEncoding   = errors.New("fasthttp: unsupported transfer-encoding")
	ErrNonNumericChars               = errors.New("fasthttp: non-numeric chars found")
	ErrNeedMore                      = errors.New("fasthttp: need more data: cannot find trailing lf")
	ErrSmallReadBuffer               = errors.New("fasthttp: small read buffer. increase readbuffersize")
)

// AddTrailerBytes add Trailer header value for chunked response
// to indicate which headers will be sent after the body.
//
// Use Set to set the trailer header later.
//
// Trailers are only supported with chunked transfer.
// Trailers allow the sender to include additional headers at the end of chunked messages.

View on GitHub (pinned to c96f600972)

Solutions

  1. Log the raw first line (wrap the conn or use curl -v against the same URL) to see what the peer actually returns.
  2. Fix the upstream server/proxy to emit RFC-compliant status lines like 'HTTP/1.1 200 OK'.
  3. Ensure the https:// scheme and TLSClientConfig are set for TLS servers so ciphertext is not parsed as plain HTTP.
  4. Retry against a healthy endpoint; fasthttp closes the connection after this parse error so the pooled conn is discarded.

Example fix

// before
req.SetRequestURI("http://svc.internal:8443/x") // TLS server, plain HTTP request
// after
req.SetRequestURI("https://svc.internal:8443/x")
client.TLSConfig = &tls.Config{ InsecureSkipVerify: true } // trust setup as needed
Defensive patterns

Strategy: type-guard

Validate before calling

// Probe the raw status line once at startup:
// curl -sv URL | head -1 must match '^HTTP/1\.[01] [0-9]{3} '

Type guard

func isBadStatusLine(err error) bool {
    return err == fasthttp.ErrUnexpectedStatusCodeChar
}

Try / catch

err := client.Do(req, resp)
if err == fasthttp.ErrUnexpectedStatusCodeChar {
    client.CloseIdleConnections() // discard possibly-corrupted pooled conns
    return retryable(fmt.Errorf("malformed status line: %w", err))
}

Prevention

When it happens

Trigger: Response.Read / Client.Do / HostClient.Do on a response whose first line is like 'HTTP/1.1 20x OK' or 'HTTP/1.1 1234abc' — any non-digit/non-separator char after the status digits (see fasthttputil/ParseUint usage in parseFirstLine).

Common situations: Talking to a misbehaving or nonconformant server/proxy, an adversarial or fuzzed peer, or a port returning proprietary protocol data instead of HTTP. Also common when TLS is missing so raw handshake bytes are parsed as HTTP.

Related errors


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