{"record":{"id":"ee8eb3ae6785ae5d","repo":"oven-sh/bun","slug":"non-ascii-character-in-string-str-this-will","errorCode":null,"errorMessage":"non-ascii character in string \"${str}\". this will not be a valid ASCIILiteral","messagePattern":"non-ascii character in string \"(.+?)\"\\. this will not be a valid ASCIILiteral","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/codegen/helpers.ts","lineNumber":35,"sourceCode":"export function readdirRecursive(root: string): string[] {\n  const files = fs.readdirSync(root, { withFileTypes: true });\n  return files.flatMap(file => {\n    const fullPath = path.join(root, file.name);\n    return file.isDirectory() ? readdirRecursive(fullPath) : fullPath;\n  });\n}\n\nexport function resolveSyncOrNull(specifier: string, from: string) {\n  try {\n    return Bun.resolveSync(specifier, from);\n  } catch {\n    return null;\n  }\n}\n\nexport function checkAscii(str: string) {\n  if (!isAscii(Buffer.from(str))) {\n    throw new Error(`non-ascii character in string \"${str}\". this will not be a valid ASCIILiteral`);\n  }\n\n  return str;\n}\n\nexport function writeIfNotChanged(file: string, contents: string) {\n  if (Array.isArray(contents)) contents = contents.join(\"\");\n  contents = contents.replaceAll(\"\\r\\n\", \"\\n\").trim() + \"\\n\";\n\n  try {\n    const oldContents = fs.readFileSync(file, \"utf8\");\n    if (oldContents === contents) {\n      return;\n    }\n  } catch (e) {}\n\n  try {\n    fs.writeFileSync(file, contents);","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/oven-sh/bun/blob/8c5296ac459e8252d3cd702f3fbcbb0c249d95d5/src/codegen/helpers.ts#L17-L53","documentation":"checkAscii (src/codegen/helpers.ts) guards strings that codegen embeds as C++ ASCIILiteral byte arrays — bundle-modules.ts checks the generated module source (line 425) and bundle-functions.ts checks each function source (line 405). A non-ASCII character would corrupt the generated literal (byte length != character length), so it throws before writing broken output.","triggerScenarios":"Adding a builtin JS module or function under src/js whose source contains a non-ASCII character (unicode strings, smart quotes, non-ASCII identifiers) that ends up in the embedded literal.","commonSituations":"Copy-pasting code with typographic quotes or accented words into src/js builtins; pasting a unicode arrow or emoji into an error message of a builtin.","solutions":["Replace the character the message prints with an ASCII equivalent or a \\uXXXX escape in the JS source.","Keep builtin module files ASCII-only (configure your editor / add an editorconfig `charset` check).","If unicode is required at runtime, encode it as an escape sequence so the literal bytes stay ASCII."],"exampleFix":"// before (src/js/builtins file)\nexport const msg = \"unexpected value”; // smart quote is non-ASCII\n\n// after\nexport const msg = \"unexpected value\";","handlingStrategy":"validation","validationCode":"import { isAscii } from \"node:buffer\";\nfunction assertAsciiLiteral(s: string, where: string) {\n  if (!isAscii(Buffer.from(s))) {\n    const bad = [...s].find(ch => ch.charCodeAt(0) > 0x7f);\n    throw new Error(`${where}: non-ASCII character ${JSON.stringify(bad)} — use \\\\u escapes`);\n  }\n}","typeGuard":"const isAsciiLiteral = (s: string) => isAscii(Buffer.from(s));","tryCatchPattern":null,"preventionTips":["Keep builtin module sources ASCII; express unicode via \\uXXXX escapes.","Add a lint step that rejects non-ASCII bytes in src/js/** before running codegen.","Beware copy-paste from docs/browsers: smart quotes and dashes are the usual offenders."],"tags":["codegen","ascii","asciiliteral","builtins"],"backgroundTag":null,"analyzedSha":"8c5296ac459e8252d3cd702f3fbcbb0c249d95d5","analyzedAt":"2026-08-16T08:01:58.794Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}