{"record":{"id":"fca12ce8a729b6f4","repo":"denoland/deno","slug":"invalidcharactererror","errorCode":"InvalidCharacterError","errorMessage":"Failed to decode base64: invalid character","messagePattern":"Failed to decode base64: invalid character","errorType":"exception","errorClass":"DOMException","httpStatus":null,"severity":"error","filePath":"ext/web/05_base64.js","lineNumber":32,"sourceCode":"  TypeErrorPrototype,\n} = primordials;\n\nconst webidl = core.loadExtScript(\"ext:deno_webidl/00_webidl.js\");\nconst { DOMException } = core.loadExtScript(\"ext:deno_web/01_dom_exception.js\");\n\n/**\n * @param {string} data\n * @returns {string}\n */\nfunction atob(data) {\n  const prefix = \"Failed to execute 'atob'\";\n  webidl.requiredArguments(arguments.length, 1, prefix);\n  data = webidl.converters.DOMString(data, prefix, \"Argument 1\");\n  try {\n    return op_base64_atob(data);\n  } catch (e) {\n    if (ObjectPrototypeIsPrototypeOf(TypeErrorPrototype, e)) {\n      throw new DOMException(\n        \"Failed to decode base64: invalid character\",\n        \"InvalidCharacterError\",\n      );\n    }\n    throw e;\n  }\n}\n\n/**\n * @param {string} data\n * @returns {string}\n */\nfunction btoa(data) {\n  const prefix = \"Failed to execute 'btoa'\";\n  webidl.requiredArguments(arguments.length, 1, prefix);\n  data = webidl.converters.DOMString(data, prefix, \"Argument 1\");\n  try {\n    return op_base64_btoa(data);","sourceCodeStart":14,"sourceCodeEnd":50,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/ext/web/05_base64.js#L14-L50","documentation":"atob forwards to the native op_base64_atob, which rejects any input outside the standard base64 alphabet (A-Z a-z 0-9 + / =) or with an impossible length; the resulting TypeError is converted to DOMException InvalidCharacterError 'Failed to decode base64: invalid character' (ext/web/05_base64.js:29-36). Note the fixed message: the specific bad character is not included. Common culprits are base64url tokens (with - and _), embedded whitespace/newlines, data: URI prefixes, and inputs whose length % 4 === 1.","triggerScenarios":"atob('hello!') ('!' is not base64); atob('a') (length % 4 === 1 is impossible); atob(jwt.split('.')[1]) where the JWT segment is base64url containing '-' or '_'; atob of a value copied with a trailing newline or a 'data:image/png;base64,' prefix.","commonSituations":"Decoding JWT header/payload segments without base64url translation; decoding data URIs pasted with the MIME prefix; tokens/API keys copied from terminals including whitespace; interop with encoders that emit base64url or omit padding.","solutions":["Normalize the input before atob: trim whitespace, strip a data: URI prefix up to the first comma, and translate base64url - to + and _ to /.","Reject early when (length without trailing '=' padding) % 4 === 1 — it can never decode.","Re-add stripped '=' padding to a multiple of 4 before calling atob.","Wrap atob in try/catch on InvalidCharacterError so callers learn which payload was malformed."],"exampleFix":"// before\nconst payload = atob(jwt.split('.')[1]); // base64url '-'/'_' -> InvalidCharacterError\n\n// after\nlet s = jwt.split('.')[1].replaceAll('-', '+').replaceAll('_', '/');\nwhile (s.length % 4 !== 0) s += '=';\nconst payload = atob(s);","handlingStrategy":"validation","validationCode":"function toStdBase64(s) {\n  let t = String(s).trim().replace(/^data:[^,]*,/, '')\n    .replaceAll('-', '+').replaceAll('_', '/');\n  if (t.replace(/=+$/, '').length % 4 === 1) throw new TypeError('invalid base64 length');\n  while (t.length % 4 !== 0) t += '=';\n  return t;\n}\nconst bytes = atob(toStdBase64(input));","typeGuard":"const isStdBase64 = (s) =>\n  /^[A-Za-z0-9+/]+={0,2}$/.test(s) && s.length % 4 !== 1;","tryCatchPattern":"try {\n  decoded = atob(input);\n} catch (e) {\n  if (e instanceof DOMException && e.name === 'InvalidCharacterError') {\n    return rejectToken(input);\n  }\n  throw e;\n}","preventionTips":["Normalize base64url (- and _) to the standard alphabet before atob","Strip whitespace and data: URI prefixes up front","Reject length % 4 === 1 inputs before decoding"],"tags":["atob","base64","decoding"],"backgroundTag":"invalid-base64","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","contentChangedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}