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.

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)

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.