valyala/fasthttp · error

unexpected first char found: expecting 0-9

Error message

unexpected first char found: expecting 0-9

What it means

errUnexpectedFirstChar is returned by parseUintBuf and parseIPv4Octet when the input does not begin with a digit 0-9. Numeric parsing requires the first byte to be a digit, so leading signs, spaces, letters, or punctuation trigger this sentinel error. It surfaces through ParseUint and ParseIPv4.

Solutions

  1. Trim leading whitespace with bytes.TrimSpace before parsing.
  2. Reject or separately handle negative numbers — ParseUint is unsigned, so use strconv.Atoi if signs are legitimate.
  3. Pre-validate the first byte: if buf[0] < '0' || buf[0] > '9', handle the input before calling ParseUint.
  4. Check the call site: a non-numeric string reaching a numeric parser usually means the wrong field was passed.

Example fix

// before
n, err := fasthttp.ParseUint(rawHeader)
// after
rawHeader = bytes.TrimSpace(rawHeader)
if len(rawHeader) == 0 || rawHeader[0] < '0' || rawHeader[0] > '9' {
    return 0, fmt.Errorf("not an unsigned number: %q", rawHeader)
}
n, err := fasthttp.ParseUint(rawHeader)
Defensive patterns

Strategy: validation

Validate before calling

func parseUintClean(b []byte) (int, error) {
    b = bytes.TrimSpace(b)
    if len(b) == 0 || b[0] < '0' || b[0] > '9' {
        return 0, fmt.Errorf("expected unsigned int, got %q", b)
    }
    return fasthttp.ParseUint(b)
}

Type guard

func startsWithDigit(b []byte) bool {
    return len(b) > 0 && b[0] >= '0' && b[0] <= '9'
}

Try / catch

n, err := fasthttp.ParseUint(b)
if err != nil && err.Error() == "unexpected first char found: expecting 0-9" {
    return 0, fmt.Errorf("%q is not a number", b)
}

Prevention

When it happens

Trigger: ParseUint([]byte(" 42")) (leading space), ParseUint([]byte("-1")) (minus sign), ParseIPv4 on values like "abc.1.2.3" or ".1.2.3".

Common situations: Whitespace-padded header values (Content-Length: ' 123'); signed numbers passed where unsigned parsing is expected; hostnames or enum strings accidentally fed to numeric parsing.

Related errors


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

Appendix: source

Thrown at bytesconv.go:277

}

// ParseUint parses uint from buf.
//
// A value too large for an int is an error rather than a wrapped result, so
// ParseUint accepts exactly the unsigned decimal strings whose value fits in an
// int on the current platform.
func ParseUint(buf []byte) (int, error) {
	v, n, err := parseUintBuf(buf)
	if n != len(buf) {
		return -1, errUnexpectedTrailingChar
	}
	return v, err
}

var (
	errEmptyInt               = errors.New("empty integer")
	errIPv4PartTooLarge       = errors.New("ip part cannot exceed 255")
	errUnexpectedFirstChar    = errors.New("unexpected first char found: expecting 0-9")
	errUnexpectedTrailingChar = errors.New("unexpected trailing char found: expecting 0-9")
	errTooLongInt             = errors.New("too long int")
)

const (
	// maxIntDiv10 is the largest accumulator that can still take another digit.
	// Anything above it overflows an int when multiplied by 10.
	maxIntDiv10 = math.MaxInt / 10

	// maxSafeIntDigits is how many leading decimal digits can never overflow an
	// int, whatever the word size: 10**18-1 fits a 64-bit int and 10**9-1 fits a
	// 32-bit one. Go defines strconv.IntSize as 32 or 64 and nothing else.
	// TestMaxSafeIntDigits checks both halves of that claim on the build's own
	// int size.
	maxSafeIntDigits = 9 * (strconv.IntSize / 32)
)

func parseUintBuf(b []byte) (int, int, error) {

View on GitHub (pinned to c96f600972)