vitessio/vitess · error
invalid escape character %q in JSON string
Error message
invalid escape character %q in JSON string
What it means
JSON only permits the escape characters \\ b f n r t u after a backslash inside a string. When unescapeJSON encounters a backslash followed by any other character, it refuses to guess and returns this error. This indicates the string was not produced by a conforming JSON encoder or has been corrupted, since silently passing the byte through could change meaning in the generated SQL.
Source
Thrown at go/mysql/json/marshal.go:465
if i+6 <= len(src) && src[i] == '\\' && src[i+1] == 'u' {
r2 := parseHex4(src[i+2 : i+6])
if r2 >= 0 {
combined := utf16.DecodeRune(r, r2)
if combined != utf8.RuneError {
dst = utf8.AppendRune(dst, combined)
i += 6
continue
}
}
}
// Lone surrogate: encode as replacement character.
dst = utf8.AppendRune(dst, utf8.RuneError)
continue
}
dst = utf8.AppendRune(dst, r)
default:
return dst, fmt.Errorf("invalid escape character %q in JSON string", src[i])
}
}
return dst, nil
}
// parseHex4 parses exactly 4 hex digits into a rune. Returns -1 on error.
func parseHex4(s []byte) rune {
var r rune
for _, ch := range s {
r <<= 4
switch {
case ch >= '0' && ch <= '9':
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:View on GitHub (pinned to 01a25a7d17)
Solutions
- Validate with json.Valid/encoding/json before writing so invalid escapes are rejected at the boundary with a familiar error.
- Re-encode the data with json.Marshal, which escapes backslashes correctly (\\\\ for a literal backslash), instead of hand-escaping.
- For literal backslashes in paths, escape them (\\\\) or replace with forward slashes where the consumer allows.
- Find and fix the non-JSON escaper in the producer pipeline that is emitting \\q-style sequences.
Example fix
// before: unescaped backslashes in a JSON string
s := `{"path": "C:\\temp\\file"}`
// after: JSON-escaped backslashes (or use json.Marshal)
s := `{"path": "C:\\\\temp\\\\file"}` Defensive patterns
Strategy: validation
Validate before calling
if !json.Valid(input) {
return errors.New("invalid JSON escape sequence in input")
} Try / catch
if err := writeJSONAsSQL(input); err != nil {
if strings.Contains(err.Error(), "invalid escape character") {
fixed, merr := reencode(jsonBackslashUnescapeAttempt(raw))
if merr == nil {
return writeJSONAsSQL(fixed)
}
return fmt.Errorf("unrecoverable JSON escaping: %w", err)
}
return err
} Prevention
- Escape JSON strings only via json.Marshal — never custom replacers
- Double backslashes when embedding Windows paths in JSON literals
- Do not pass strings through non-JSON escapers (shell, strconv) before serialization
- Include backslash-heavy payloads in test fixtures
When it happens
Trigger: Serializing a JSON string value or object key containing an escape like \\q, \\', \\x41, \\0, or a Windows path written as \\Users\\name inside a JSON literal that was hand-built or produced by a non-JSON escaper. Reached via writeStringContent -> unescapeJSON, for both top-level strings and object keys.
Common situations: Hand-written SQL/JSON literals where backslashes were not doubled per JSON rules; data serialized by a language's native string escaper (e.g. Go strconv.Quote-ish or shell escaping) instead of a JSON encoder; log-processing pipelines that mangle backslashes; config values such as Windows file paths pasted into JSON.
Related errors
- reading JSON object key: %w
- invalid hex digit in \u escape: %q
- range %d should be >= %d
- unexpected character %q in JSON
- expected ',' or '}' in object, got %q
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/ac02657e6374006a.
Report an issue: GitHub.