{"record":{"id":"2d7fc4701625c414","repo":"gchq/CyberChef","slug":"invalid-input-xml","errorCode":null,"errorMessage":"Invalid input XML.","messagePattern":"Invalid input XML\\.","errorType":"exception","errorClass":"OperationError","httpStatus":null,"severity":"error","filePath":"src/core/operations/XPathExpression.mjs","lineNumber":61,"sourceCode":"    /**\n     * @param {string} input\n     * @param {Object[]} args\n     * @returns {string}\n     */\n    run(input, args) {\n        const [query, delimiter] = args;\n\n        let doc;\n        try {\n            doc = new xmldom.DOMParser({\n                errorHandler: {\n                    fatalError(e) {\n                        throw e;\n                    }\n                }\n            }).parseFromString(input, \"application/xml\");\n        } catch (err) {\n            throw new OperationError(\"Invalid input XML.\");\n        }\n\n        let nodes;\n        try {\n            nodes = xpath.parse(query).select({ node: doc, allowAnyNamespaceForNoPrefix: true });\n        } catch (err) {\n            throw new OperationError(`Invalid XPath. Details:\\n${err.message}.`);\n        }\n\n        const nodeToString = function(node) {\n            return node.toString();\n        };\n\n        return nodes.map(nodeToString).join(delimiter);\n    }\n\n}\n","sourceCodeStart":43,"sourceCodeEnd":79,"githubUrl":"https://github.com/gchq/CyberChef/blob/4290ea753912378913b1f3f54e0fc5720afeda5d/src/core/operations/XPathExpression.mjs#L43-L79","documentation":"Thrown by XPathExpression.run when xmldom's DOMParser raises a fatal error parsing the input string as application/xml. The operation installs a custom errorHandler that re-throws fatal errors; any throw is caught and re-wrapped as an OperationError with the fixed message 'Invalid input XML.' (the original parse detail is discarded).","triggerScenarios":"The input to the operation is not well-formed XML: unclosed tags, stray characters before the declaration, mismatched encoding, or feeding HTML/JSON/plain text into an XPath step.","commonSituations":"Chaining an operation whose output is not XML (e.g. From Hex, Deflate) directly into XPath; a recipe that assumes XML but receives a truncated/partial document; BOM or non-UTF8 bytes that break the prolog.","solutions":["Verify the input is well-formed XML before the XPath step — test it in a separate XML validation step or with xmldom/DOMParser in isolation.","Insert a 'To Base64'/'From Base64' or decode step if the bytes are encoded; strip a leading BOM or non-XML prefix.","If you genuinely have HTML, convert/clean it to XHTML first, since xmldom does not tidy malformed markup."],"exampleFix":"// before: feeding raw HTML/JSON into XPath\nchef.bake(\"<broken><tag></broken>\", [{op:\"XPath Expression\", args:[\"//tag\",\"\\n\"]}]);\n// after: ensure well-formed XML\nchef.bake(\"<root><tag>x</tag></root>\", [{op:\"XPath Expression\", args:[\"//tag\",\"\\n\"]}]);","handlingStrategy":"validation","validationCode":"import { DOMParser } from \"@xmldom/xmldom\";\nfunction isWellFormedXml(s) {\n  try { new DOMParser({errorHandler:{fatalError:()=>{throw 0;}}}).parseFromString(s,\"application/xml\"); return true; } catch { return false; }\n}","typeGuard":"const looksLikeXml = (s) => typeof s === \"string\" && /^\\s*<[^>]+>/.test(s) && /<[\\w:-]+/.test(s);","tryCatchPattern":"try { result = chef.bake(input, recipe); } catch (e) { if (/Invalid input XML/.test(e.message)) { /* pre-clean or convert input */ } else throw e; }","preventionTips":["Validate XML well-formedness before the XPath step.","Decode encoded input (Base64/Hex) before expecting XML.","Strip BOM and non-XML prefixes."],"tags":["xpath","xml","parse-error","xmldom"],"backgroundTag":null,"analyzedSha":"4290ea753912378913b1f3f54e0fc5720afeda5d","analyzedAt":"2026-08-13T06:05:50.210Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}