vitessio/vitess · error
invalid number at position %d in JSON
Error message
invalid number at position %d in JSON
What it means
writeNumber validates the numeric token starting at the current position using the SQL parser's readFloat, which enforces the JSON/SQL number grammar. If no valid number can be read (ok is false, or zero length was consumed), the writer reports the failure with the offset in the input document. This rejects malformed numbers like 1+2, 1..2, or 1e+ that a naive character-class check would accept, ensuring the generated CAST(...) expression contains only well-formed literals.
Source
Thrown at go/mysql/json/marshal.go:496
r |= rune(ch - '0')
case ch >= 'a' && ch <= 'f':
r |= rune(ch - 'a' + 10)
case ch >= 'A' && ch <= 'F':
r |= rune(ch - 'A' + 10)
default:
return -1
}
}
return r
}
func (w *sqlWriter) writeNumber(top bool) error {
// Use the parser's readFloat to validate number grammar, rejecting
// malformed inputs like "1+2", "1..2", or "1e+" that a simple
// character-class loop would accept.
n, _, ok := readFloat(w.data[w.pos:])
if !ok || n == 0 {
return fmt.Errorf("invalid number at position %d in JSON", w.pos)
}
if top {
w.buf.WriteString("CAST(")
}
w.buf.Write(w.data[w.pos : w.pos+n])
w.pos += n
if top {
w.buf.WriteString(" as JSON)")
}
return nil
}
func (w *sqlWriter) writeBool(top bool) error {
if top {
w.buf.WriteString("CAST(_utf8mb4'")
}
if w.pos+4 <= len(w.data) && string(w.data[w.pos:w.pos+4]) == "true" {
w.buf.WriteString("true")View on GitHub (pinned to 01a25a7d17)
Solutions
- Validate the whole document with encoding/json (json.Valid/Unmarshal) before writing; the standard parser rejects these number forms with position information.
- Fix the number at the reported position to a valid JSON literal (remove the stray + or extra ., supply exponent digits, e.g. 1e10).
- Replace string formatting of numbers in the producer with json.Marshal of typed numeric values so Go's formatter emits valid literals.
- Check for data corruption/truncation upstream if numbers in otherwise valid documents are being mangled.
Example fix
// before: formatting artifacts produce invalid tokens
s := fmt.Sprintf(`{"v": %s+}`, numStr) // e.g. 1e+
// after: marshal typed values
b, _ := json.Marshal(map[string]float64{"v": v}) Defensive patterns
Strategy: validation
Validate before calling
if !json.Valid(input) {
return fmt.Errorf("invalid JSON number in document")
} Try / catch
if err := writeJSONAsSQL(input); err != nil {
if strings.Contains(err.Error(), "invalid number at position") {
var se *json.SyntaxError
if e := json.Unmarshal(raw, new(any)); e != nil && errors.As(e, &se) {
return fmt.Errorf("bad number near offset %d", se.Offset)
}
return err
}
return err
} Prevention
- Emit numbers as typed values through json.Marshal, not formatted strings
- Avoid %s-formatting numeric strings that may carry + or trailing signs
- Validate documents at the ingestion boundary with json.Valid
- Reject inputs with arithmetic-like tokens (1+2, 1..2) before processing
When it happens
Trigger: Passing a JSON document to the writer whose number token is malformed at w.pos: two decimal points (1..2), an exponent with no digits (1e+, 1e-), characters not part of a number (1+2 where a value was expected), a bare minus with no digits (-), or leading characters like +2 or 0x1F that JSON numbers do not allow.
Common situations: Custom code that formats numbers into JSON strings using fmt.Sprintf with wrong verbs producing artifacts (1e+ without exponent digits after trimming); log or CSV-to-JSON converters emitting raw arithmetic expressions; corrupted payloads where digits were dropped or replaced; hand-written fixtures with typos.
Related errors
- reading JSON object key: %w
- expected ',' or ']' in array, got %q
- invalid number in JSON string: %q
- overflow
- unsupported format specifier: %%%c
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/ce756c695eb45d6c.
Report an issue: GitHub.