{"id":"5f17c983bfec99ee","repo":"google/gson","slug":"string-contains-non-ascii-characters-s-location","errorCode":null,"errorMessage":"String contains non-ASCII characters: {s}{location}","messagePattern":"String contains non-ASCII characters: (.+?)(.+?)","errorType":"exception","errorClass":"MalformedJsonException","httpStatus":null,"severity":"error","filePath":"gson/src/main/java/com/google/gson/internal/bind/JsonTreeReader.java","lineNumber":431,"sourceCode":"  }\n\n  private String locationString() {\n    return \" at path \" + getPath();\n  }\n\n  /** Returns whether every character of {@code s} is ASCII (code point at most 127). */\n  public static boolean isAllAscii(String s) {\n    for (int i = 0; i < s.length(); i++) {\n      if (s.charAt(i) > 127) {\n        return false;\n      }\n    }\n    return true;\n  }\n\n  private void validateAscii(String s) throws MalformedJsonException {\n    if (!isAllAscii(s)) {\n      throw new MalformedJsonException(\n          \"String contains non-ASCII characters: \" + s + locationString());\n    }\n  }\n\n  /** Creates a {@link NumberFormatException} whose message includes the current path. */\n  private NumberFormatException numberFormatException(String message, NumberFormatException cause) {\n    NumberFormatException exception = new NumberFormatException(message + locationString());\n    exception.initCause(cause);\n    return exception;\n  }\n}\n","sourceCodeStart":413,"sourceCodeEnd":443,"githubUrl":"https://github.com/google/gson/blob/8b8628c65699bc4421696183c62ae0c1b9b281dc/gson/src/main/java/com/google/gson/internal/bind/JsonTreeReader.java#L413-L443","documentation":"Thrown by JsonTreeReader.validateAscii() when nextLong() or nextInt() reads a STRING token whose content contains characters above code point 127. Gson only attempts ASCII numeric coercion for string-encoded numbers; any non-ASCII byte (e.g. Unicode digits, whitespace, or unit suffixes) is treated as malformed. It is a MalformedJsonException because the data cannot be parsed as the requested numeric type under strict ASCII rules.","triggerScenarios":"Deserializing a field declared as long/int whose JSON value is a quoted string containing non-ASCII characters (e.g. \"\\u0661\\u0662\" Arabic-Indic digits, \"１２３\" fullwidth digits, a value with a stray BOM or currency symbol). The path is only exercised when peek()==STRING in nextLong()/nextInt(), which happens when Gson reads a string-typed JSON value into a numeric Java field.","commonSituations":"Locale-specific numeric strings from external APIs (fullwidth digits from Japanese/Chinese systems, Arabic-Indic digits); copy-paste introducing zero-width or BOM characters; data exported from spreadsheets that embed currency/grouping symbols; misconfigured encodings where binary garbage lands in a numeric field.","solutions":["Sanitize the source string to ASCII before deserialization, or fix the producing system to emit plain ASCII numeric literals.","Change the target field type to String and parse it yourself with a NumberFormat that handles the locale, then convert to long/int.","Register a custom TypeAdapter<Long> that strips non-ASCII and uses Long.parseLong, or use @JsonAdapter to attach it per field.","If the value is genuinely numeric but Unicode-encoded, normalize via StringNormalizer (NFKC) to ASCII digits before parsing."],"exampleFix":"// before: field is long but JSON has \"\\u0661\\u0662\" (Arabic 12) -> throws\nclass Data { long count; }\n\n// after: accept String, normalize, parse manually\nclass Data {\n  String count;\n  long getCount() {\n    return Long.parseLong(java.text.Normalizer.normalize(count, java.text.Normalizer.Form.NFKC)\n        .replaceAll(\"[^0-9-]\", \"\"));\n  }\n}","handlingStrategy":"validation","validationCode":"// Validate ASCII-ness of a string before it is read as a number\nif (reader.peek() == JsonToken.STRING) {\n  String s = reader.nextString();\n  if (!JsonTreeReader.isAllAscii(s)) {\n    s = java.text.Normalizer.normalize(s, java.text.Normalizer.Form.NFKC).replaceAll(\"[^\\\\x00-\\\\x7F]\", \"\");\n  }\n  return Long.parseLong(s);\n}","typeGuard":null,"tryCatchPattern":"try {\n  return reader.nextLong();\n} catch (MalformedJsonException e) {\n  if (e.getMessage() != null && e.getMessage().startsWith(\"String contains non-ASCII\")) {\n    // fall back to String + manual normalization\n    String s = reader.nextString();\n    return Long.parseLong(java.text.Normalizer.normalize(s, java.text.Normalizer.Form.NFKC).replaceAll(\"[^0-9-]\", \"\"));\n  }\n  throw e;\n}","preventionTips":["Validate incoming numeric strings at the API boundary; reject or normalize non-ASCII.","Prefer numeric JSON literals (unquoted) over strings when you control the producer.","Type ambiguous numeric fields as String and parse explicitly with locale-aware NumberFormat."],"tags":["json","parsing","encoding","ascii","numeric","gson"],"analyzedSha":"8b8628c65699bc4421696183c62ae0c1b9b281dc","analyzedAt":"2026-08-04T19:12:22.202Z","schemaVersion":2}