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

  1. Validate with json.Valid/encoding/json before writing so invalid escapes are rejected at the boundary with a familiar error.
  2. Re-encode the data with json.Marshal, which escapes backslashes correctly (\\\\ for a literal backslash), instead of hand-escaping.
  3. For literal backslashes in paths, escape them (\\\\) or replace with forward slashes where the consumer allows.
  4. 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

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


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