valyala/fasthttp · error

unexpected trailing char found: expecting 0-9

Error message

unexpected trailing char found: expecting 0-9

What it means

errUnexpectedTrailingChar is returned by ParseUint and parseIPv4Octet when digits are followed by a non-digit character. ParseUint consumes all input and requires it to be entirely numeric, so values like "12x" or "1.5" fail with this error. It signals malformed numeric input with trailing garbage.

Source

Thrown at bytesconv.go:278

// 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) {
	if len(b) == 0 {

View on GitHub (pinned to c96f600972)

Solutions

  1. Validate the whole string is digits (e.g. bytes.IndexFunc(buf, nonDigit) < 0) before parsing.
  2. Use strconv.ParseFloat / ParseInt directly if the input legitimately contains decimals or signs — fasthttp's ParseUint is deliberately strict.
  3. Split the input on the expected delimiter first (e.g. cut the port off 'host:port') and parse only the numeric part.
  4. Return 400 for client-supplied values since this is malformed user input, not a server fault.

Example fix

// before
n, err := fasthttp.ParseUint([]byte("1.5"))
// after
f, err := strconv.ParseFloat("1.5", 64) // floats need a float parser
// or, for strict uint:
if !isAllDigits(buf) { return 0, errors.New("expected unsigned integer") }
n, err := fasthttp.ParseUint(buf)
Defensive patterns

Strategy: validation

Validate before calling

func parseUintStrict(b []byte) (int, error) {
    if !digitsOnly(b) {
        return 0, fmt.Errorf("trailing characters in %q", b)
    }
    return fasthttp.ParseUint(b)
}
func digitsOnly(b []byte) bool {
    return len(b) > 0 && bytes.IndexFunc(b, func(r rune) bool { return r < '0' || r > '9' }) < 0
}

Type guard

func isAllDigits(b []byte) bool {
    return len(b) > 0 && bytes.IndexFunc(b, func(r rune) bool { return r < '0' || r > '9' }) < 0
}

Try / catch

n, err := fasthttp.ParseUint(b)
if err != nil && err.Error() == "unexpected trailing char found: expecting 0-9" {
    return 0, fmt.Errorf("%q must be a plain integer", b)
}

Prevention

When it happens

Trigger: ParseUint([]byte("8080/tcp")) (EXPOSE-style values), ParseUint([]byte("1.5")) (float passed to int parser), ParseIPv4 with a component like "1a.2.3.4"; also Args.GetUint where the value has trailing characters.

Common situations: Float values sent to integer parsers; values with units ('100ms', '5kb'); header values with inline comments or ports; form fields where clients append stray characters.

Related errors


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