{"record":{"id":"513b78d0a2eb190b","repo":"apple/pkl","slug":"valid-string-character","errorCode":null,"errorMessage":"valid string character","messagePattern":"valid string character","errorType":"exception","errorClass":"ParseException","httpStatus":null,"severity":"error","filePath":"pkl-core/src/main/java/org/pkl/core/util/json/JsonParser.java","lineNumber":258,"sourceCode":"      throw expected(\"'\" + ch + \"'\");\n    }\n  }\n\n  private void readString() throws IOException {\n    handler.startString();\n    handler.endString(readStringInternal());\n  }\n\n  private String readStringInternal() throws IOException {\n    read();\n    startCapture();\n    while (current != '\"') {\n      if (current == '\\\\') {\n        pauseCapture();\n        readEscape();\n        startCapture();\n      } else if (current < 0x20) {\n        throw expected(\"valid string character\");\n      } else {\n        read();\n      }\n    }\n    var string = endCapture();\n    read();\n    return string;\n  }\n\n  private void readEscape() throws IOException {\n    read();\n    switch (current) {\n      case '\"', '/', '\\\\' -> captureBuffer.append((char) current);\n      case 'b' -> captureBuffer.append('\\b');\n      case 'f' -> captureBuffer.append('\\f');\n      case 'n' -> captureBuffer.append('\\n');\n      case 'r' -> captureBuffer.append('\\r');\n      case 't' -> captureBuffer.append('\\t');","sourceCodeStart":240,"sourceCodeEnd":276,"githubUrl":"https://github.com/apple/pkl/blob/f3efcbfc9b60d30053b0536d664948d7aa1b8673/pkl-core/src/main/java/org/pkl/core/util/json/JsonParser.java#L240-L276","documentation":"Inside a JSON string, all characters must be either an escape sequence (backslash-prefixed) or a character >= 0x20. readStringInternal throws expected(\"valid string character\") when it encounters an unescaped control character (e.g. raw tab, newline, or any byte < 0x20) inside a string literal. Strict JSON forbids literal control characters in strings.","triggerScenarios":"parse() on strings containing raw newlines/tabs (multi-line strings pasted into JSON), raw escape bytes like 0x0B, or binary/corrupt data inside a quoted string.","commonSituations":"Copy-pasting multi-line text into a JSON string without escaping newlines, log files or binary blobs embedded in JSON, Windows-generated files with stray control characters, heredocs embedded in config.","solutions":["Escape control characters: use \\n, \\t, \\r, \\uXXXX instead of raw bytes inside the string.","Serialize the text with a proper JSON encoder (JSON.stringify / Jackson / json.dumps) instead of hand-concatenating strings into JSON.","Strip or replace invalid control characters (chars < 0x20) from the input before parsing if the data is known to be dirty.","Use the ParseException's line/column to find the offending character in the document."],"exampleFix":"// before\nString json = \"{\\\"text\\\": \\\"line1\nline2\\\"}\"; // raw newline in string\n// after\nString json = \"{\\\"text\\\": \\\"line1\\\\nline2\\\"}\";","handlingStrategy":"validation","validationCode":"// Java: reject raw control characters inside strings before parsing\nstatic boolean hasRawControlChars(String json) {\n  boolean inStr = false, esc = false;\n  for (char c : json.toCharArray()) {\n    if (esc) { esc = false; continue; }\n    if (c == '\\\\' && inStr) { esc = true; continue; }\n    if (c == '\"') inStr = !inStr;\n    else if (inStr && c < 0x20) return true;\n  }\n  return false;\n}","typeGuard":null,"tryCatchPattern":"try {\n  parser.parse(json);\n} catch (ParseException e) {\n  if (e.getMessage().contains(\"valid string character\")) {\n    throw new IllegalArgumentException(\"Unescaped control character in JSON string at \" + e.getLocation().line + \":\" + e.getLocation().column, e);\n  }\n  throw e;\n}","preventionTips":["Escape \\n, \\t, \\r in strings; never embed raw newlines.","Build JSON with a serializer that escapes automatically.","Sanitize text from logs/binary sources before embedding it in JSON.","Detect and strip stray control bytes from externally produced files."],"tags":["json","parser","string-escaping","pkl"],"backgroundTag":"json-parse-error","analyzedSha":"f3efcbfc9b60d30053b0536d664948d7aa1b8673","analyzedAt":"2026-09-08T13:10:45.570Z","contentChangedAt":"2026-09-08T13:10:45.570Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}