vitessio/vitess · error

cannot parse uint64 from %q

Error message

cannot parse uint64 from %q

What it means

After skipping leading whitespace, parseUint64 found no parseable characters left (the string was all whitespace, or empty of digits, or only a sign that is not allowed). It returns this error quoting the original string, together with the best-effort value 0.

Source

Thrown at go/mysql/fastparse/fastparse.go:55

// It is equivalent to strconv.ParseUint(s, base, 64) in case it succeeds,
// but on error it will return the best effort value of what it has parsed so far.
func parseUint64(s string, base int, allowNeg bool) (uint64, error) {
	if len(s) == 0 {
		return 0, errors.New("cannot parse uint64 from empty string")
	}
	if base < 2 || base > 36 {
		return 0, fmt.Errorf("invalid base %d; must be in [2, 36]", base)
	}
	i := uint(0)
	for i < uint(len(s)) {
		if !isSpace(s[i]) {
			break
		}
		i++
	}

	if i >= uint(len(s)) {
		return 0, fmt.Errorf("cannot parse uint64 from %q", s)
	}
	// For some reason, MySQL parses things as uint64 even with
	// a negative sign and then turns it into the 2s complement value.
	minus := s[i] == '-'
	if minus {
		if !allowNeg {
			return 0, fmt.Errorf("cannot parse uint64 from %q", s)
		}
		i++
		if i >= uint(len(s)) {
			return 0, fmt.Errorf("cannot parse uint64 from %q", s)
		}
	}

	d := uint64(0)
	j := i
next:
	for i < uint(len(s)) {

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Trim and validate the input is a non-empty numeric string before calling ParseUint64.
  2. Treat the returned error as MySQL would: coerce to the returned best-effort 0 value if that matches SQL semantics, or propagate a conversion error to the client.
  3. If negatives are legitimate, use ParseUint64WithNeg (allowNeg=true) so "-5" parses as the 2s-complement value.
  4. Handle empty-string input at a higher layer (e.g. return 0 or NULL) instead of routing it to the parser.

Example fix

// before
v, err := fastparse.ParseUint64(userInput, 10)

// after
input := strings.TrimSpace(userInput)
if input == "" {
    return 0, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "empty numeric value")
}
v, err := fastparse.ParseUint64(input, 10)
Defensive patterns

Strategy: validation

Validate before calling

func isParseableUint(s string, allowNeg bool) bool {
    s = strings.TrimSpace(s)
    if s == "" {
        return false
    }
    if s[0] == '-' {
        return allowNeg && len(s) > 1
    }
    if s[0] == '+' {
        s = s[1:]
    }
    if s == "" {
        return false
    }
    for _, c := range s {
        if c < '0' || c > '9' {
            return false
        }
    }
    return true
}

Try / catch

v, err := fastparse.ParseUint64(s, 10)
if err != nil {
    if strings.Contains(err.Error(), "cannot parse uint64") {
        // match MySQL coercion semantics: use best-effort value 0
        v = 0
    }
}

Prevention

When it happens

Trigger: Calling fastparse.ParseUint64 (allowNeg=false) or ParseUint64WithNeg with strings like " ", "\t\n", "-5" when negatives are disallowed, "+" alone, or any string whose first non-space byte is not a digit (or a '-' when allowNeg is true).

Common situations: Parsing empty/blank user input or empty query parameters as numbers; SQL string values like '' or '-' converted to integers in expression evaluation; config values that are unset strings; clients sending non-numeric values where MySQL would coerce to 0 with warnings.

Related errors


AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01). Data as JSON: /api/errors/e7050da6134806bd. Report an issue: GitHub.