{"record":{"id":"035fec3bfccf6368","repo":"swc-project/swc","slug":"unexpected-null-character","errorCode":null,"errorMessage":"Unexpected null character","messagePattern":"Unexpected null character","errorType":"exception","errorClass":"swc_html_parser::error::Error","httpStatus":null,"severity":"warning","filePath":"crates/swc_html_parser/src/parser/mod.rs","lineNumber":668,"sourceCode":"        if self.is_fragment_case && self.open_elements_stack.items.len() == 1 {\n            return self.context_element.as_ref();\n        }\n\n        self.open_elements_stack.items.last()\n    }\n\n    fn process_token_in_foreign_content(\n        &mut self,\n        token_and_info: &mut TokenAndInfo,\n    ) -> PResult<()> {\n        let TokenAndInfo { token, .. } = &token_and_info;\n\n        match token {\n            // A character token that is U+0000 NULL\n            //\n            // Parse error. Insert a U+FFFD REPLACEMENT CHARACTER character.\n            Token::Character { value, .. } if *value == '\\x00' => {\n                self.errors.push(Error::new(\n                    token_and_info.span,\n                    ErrorKind::UnexpectedNullCharacter,\n                ));\n\n                token_and_info.token = Token::Character {\n                    value: '\\u{FFFD}',\n                    raw: Some(Raw::Atom(Atom::new(String::from('\\x00')))),\n                };\n\n                println!(\"{:?}\", token_and_info.token);\n\n                self.insert_character(token_and_info)?;\n            }\n            // A character token that is one of U+0009 CHARACTER TABULATION, U+000A LINE FEED (LF),\n            // U+000C FORM FEED (FF), U+000D CARRIAGE RETURN (CR), or U+0020 SPACE\n            //\n            // Insert the token's character.\n            Token::Character {","sourceCodeStart":650,"sourceCodeEnd":686,"githubUrl":"https://github.com/swc-project/swc/blob/5176682b65416c6b5de6b47379ae1588ea3ecb3f/crates/swc_html_parser/src/parser/mod.rs#L650-L686","documentation":"Thrown by process_token_in_foreign_content (crates/swc_html_parser/src/parser/mod.rs:668) when a character token U+0000 NULL arrives while the adjusted current node is an SVG/MathML element. Per the HTML5 spec this is a parse error: the parser records UnexpectedNullCharacter, rewrites the token to U+FFFD REPLACEMENT CHARACTER, and keeps inserting text, so the document is still produced. A literal NUL in the input almost always signals upstream data corruption (truncated buffers, UTF-16 read as bytes, C-string terminators leaking in).","triggerScenarios":"Calling Parser::parse_document or parse_file_as_document on input where a literal \\x00 byte sits inside an <svg>...</svg> or <math>...</math> subtree, e.g. `<svg>te\\x00xt</svg>`. The foreign-content dispatcher (not the HTML in-body path) is what routes the character token into this arm, so the same byte in plain HTML body text reports the lexer's UnexpectedNullCharacter instead.","commonSituations":"Reading files with from_utf8_unchecked over truncated buffers, concatenating NUL-terminated C strings into HTML, decoding UTF-16 content with leftover NULs, or property/fuzz tests feeding arbitrary bytes. In this arm swc also contains a leftover debug println! that dumps the replacement token to stdout on every occurrence.","solutions":["Strip NUL bytes at the ingestion boundary before parsing (html.replace('\\0', \"\") or map to U+FFFD to match the parser's own recovery)","Fix the producer: validate with String::from_utf8 / detect UTF-16 BOM before handing data to the parser","If NUL bytes are expected and the U+FFFD recovery is acceptable, filter ErrorKind::UnexpectedNullCharacter out of take_errors()","In fuzz/property tests, discard inputs containing \\x00 before asserting a clean parse"],"exampleFix":"// before\nlet fm = cm.new_source_file(file.into(), raw_with_nuls);\nlet doc = parse_file_as_document(&fm, config, &mut errors)?;\n\n// after\nlet sanitized = raw_with_nuls.replace('\\0', \"\\u{FFFD}\");\nlet fm = cm.new_source_file(file.into(), sanitized);\nlet doc = parse_file_as_document(&fm, config, &mut errors)?;","handlingStrategy":"validation","validationCode":"fn contains_null_bytes(html: &str) -> bool {\n    html.as_bytes().contains(&0)\n}\n\nif contains_null_bytes(&html) {\n    html = html.replace('\\0', \"\\u{FFFD}\"); // same recovery the parser applies\n}","typeGuard":null,"tryCatchPattern":"use swc_html_parser::error::ErrorKind;\n\nlet mut errors = Vec::new();\nlet doc = swc_html_parser::parse_file_as_document(&fm, config, &mut errors)?;\n\n// Recoverable: the NUL was already rewritten to U+FFFD in the tree.\nfor err in &errors {\n    if matches!(err.kind(), ErrorKind::UnexpectedNullCharacter) {\n        log::warn!(\"null byte in foreign content; input source may be corrupt\");\n    }\n}","preventionTips":["Validate encoding (String::from_utf8) before parsing; NUL bytes usually mean UTF-16 or binary mishandling","Strip or map NUL bytes once at the ingestion boundary, not per parse call","Treat UnexpectedNullCharacter in test suites as an upstream-corruption signal, not a parser defect"],"tags":["html","parser","null-byte","encoding","foreign-content","swc"],"backgroundTag":"null-byte-in-input","analyzedSha":"5176682b65416c6b5de6b47379ae1588ea3ecb3f","analyzedAt":"2026-08-17T16:16:52.067Z","contentChangedAt":"2026-08-17T16:16:52.067Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}