XML Fixer
Paste XML a parser rejects and get a well-formed document back
Input
Repaired XML
What the XML Fixer does
XML parsers are famously unforgiving, and that is by design. The specification calls it a fatal error: the moment a document stops being well-formed, a conforming processor is required to stop and report it rather than guess. So one bare & in a product description, three thousand lines into a feed, and you get EntityRef: expecting ';' and nothing else. No partial tree, no usable data, no hint about the other four problems waiting behind it.
This page takes the rejected document and hands back one that parses. Open tags get their closing partners, a </Status> that was meant to close <status> is matched up, attribute values that lost their quotes get them back, raw & and < characters inside text become & and <, and a document that somehow ended up with two top-level elements is given a single root to live under. What comes out is well-formed XML in the sense that the W3C XML 1.0 recommendation means it.
Worth being clear about the boundary, though. Well-formed is not the same as valid. Well-formedness is pure syntax — tags nest, entities resolve, there is exactly one root. Validity means the document also obeys a DTD or an XSD: the right elements in the right order with the right types. This page fixes the first kind of problem. If your document parses fine and your schema still rejects it, nothing here will help, because the disagreement is about meaning rather than punctuation.
A repair is also an inference about what you meant. When a <plan> never closes, there is normally one obvious place the closing tag belongs, and that is where it goes — but read the right-hand panel before you ship the result. If you would rather see the error than have it corrected, the XML Validator reports the line and column and leaves your bytes untouched.
How to repair a broken XML document
- Paste the document that fails – Drop the XML into the left panel. Upload reads a .xml or .txt file straight off disk without checking it first, which matters here — the whole point is that the file does not parse yet. Sample loads a deliberately mangled SIM provisioning response you can experiment with.
- Press Fix XML – The button sits at the top of the input panel and stays disabled while a repair is running, so a slow response cannot turn into two requests against the same document.
- Read the repaired document – The result appears on the right. Skim the tag structure first: check that nothing was nested one level deeper than you expected, which is the usual side effect of a missing closing tag being placed at the wrong depth.
- Check your text nodes – Escaping changes characters inside your content, so this is the part worth reading closely. An address containing "Marks & Spencer" should come back as "Marks &amp; Spencer" — same text, legal syntax. If a value looks shortened rather than escaped, something was ambiguous and you should look at the source.
- Verify long identifiers survived – ICCIDs, IMSIs and account numbers run to 18 or 19 digits. The repaired text goes into the editor as text and is never routed through a numeric type, so every digit you paste in is a digit you get back.
- Copy, minify or download – Copy puts the result on your clipboard, Minify collapses the whitespace between elements for a compact payload, and Download saves it as fixed.xml.
Pro tip: if the repair looks wrong, the most likely cause is a document that was truncated mid-element. A file that stops halfway through a tree has no single correct completion — the fixer has to pick a nesting depth to close everything at, and it may not pick yours. Fetch the complete document and try again.
Example: a cell report the parser will not touch
Three separate problems in six lines, which is entirely typical of hand-edited XML. The attribute value lost its quotes, the operator name contains a bare ampersand, and someone capitalised the closing tag. A parser reports only the first one it reaches.
<?xml version="1.0" encoding="UTF-8"?> <cellReport> <cell id=4021> <rsrp>-97</rsrp> <operator>Vodafone & O2</operator> </Cell> </cellReport>
<?xml version="1.0" encoding="UTF-8"?> <cellReport> <cell id="4021"> <rsrp>-97</rsrp> <operator>Vodafone & O2</operator> </cell> </cellReport>
Nine ways XML stops being well-formed
Each of these produces a fatal error, and the message you get names a byte offset rather than a cause. Here is what the parser is really complaining about in each case, and why the mistake is so easy to make in the first place.
A tag that never closes
<plan> <dataGb>50</dataGb> </subscriber>
Every non-empty element needs a matching end tag — there is no HTML-style optional close in XML, and <br> on its own is an error rather than a shortcut. libxml2 reports this as Opening and ending tag mismatch: plan line 4 and subscriber, which points at the closing tag; the real mistake is four lines earlier. If an element genuinely has no content, write it as <plan/>.
Open and close tags that do not match
<status>active</Status>
XML element names are case-sensitive, always, with no exceptions and no configuration flag. <status> and </Status> are two unrelated names as far as the parser is concerned. Anyone arriving from HTML — where the browser quietly lowercases everything for you — hits this within their first hour. MDN's XML introduction is blunt about it being one of the handful of rules that separates the two languages.
A bare ampersand in text
<operator>Vodafone & O2</operator>
& starts an entity reference, so the parser reads ahead expecting a name and a semicolon and instead finds a space. It has to be written &. XML predefines exactly five entities — &, <, >, ' and " — and that is the whole list; is an HTML thing and will fail here unless you declare it yourself. See section 4.6 of the XML specification. Query strings are the classic source: a URL with ?a=1&b=2 pasted straight into an element breaks the file.
An unescaped < inside content
<rule>rsrp < -110 dBm</rule>
A < in character data always means "a tag starts here", so the parser tries to read -110 as an element name, fails on the leading hyphen, and reports something opaque like StartTag: invalid element name. Write it as <, or wrap the whole value in <![CDATA[ ... ]]> if it is a chunk of text full of markup-ish characters. Note that > is legal in content — it only needs escaping in the accidental sequence ]]>.
An attribute value with no quotes
<cell id=4021 tac="17">
HTML allows unquoted attribute values; XML does not, not even for a plain integer. The AttValue production requires either single or double quotes around every value. Single quotes are perfectly legal, incidentally, which is handy when the value itself contains a double quote — but you cannot leave them off entirely.
Two root elements
<subscriber>…</subscriber> <subscriber>…</subscriber>
An XML document has exactly one outermost element. Two siblings at the top level produce Extra content at the end of the document, which is a genuinely misleading message — nothing is wrong with the second element, only with the fact that it exists at that level. This happens whenever a script appends records to a file in a loop, or when someone cats a directory of per-record exports together. The fix is a wrapper such as <subscribers> around the lot.
A declaration that is not at the very start
␣ <?xml version="1.0" encoding="UTF-8"?> <cellReport>
The XML declaration must be the first thing in the file — byte zero, no leading whitespace, no blank line, no comment, and no UTF-8 byte order mark either. You get XML declaration allowed only at the start of the document, and the cause is invisible in most editors. It shows up constantly in templated output, where a stray newline after a PHP tag or a Jinja block lands ahead of the declaration. The same trap catches a second <?xml ... ?> left behind when two documents are stitched together.
A namespace prefix nobody declared
<provisioning> <sim:iccid>8901240544102066246</sim:iccid> </provisioning>
A colon in a name means the part before it is a namespace prefix, and every prefix has to be bound by an xmlns: attribute somewhere up the tree. Here nothing binds sim, so a namespace-aware parser reports Namespace prefix sim on iccid is not defined. It almost always means an element was lifted out of a larger document and left its xmlns:sim="urn:telco:sim" declaration behind on the old root. Namespaces in XML is the spec for this, and it is a separate document from XML 1.0 itself — which is exactly why some tools accept the file and others do not.
An element name that is not a legal name
<2gCoverage>yes</2gCoverage> <signal strength>-97</signal strength>
Names must start with a letter or an underscore — never a digit — and cannot contain spaces. The Name production spells out the exact character ranges. This is the standard failure mode of code that converts a spreadsheet or a CSV header row into XML by wrapping each column name in angle brackets: 2G Coverage becomes an element name and the whole export is unusable. Prefix the digit or replace the space with an underscore and it parses.
If you are looking at a message you have not seen before, the wording differs sharply between implementations for the same broken byte — libxml2, Java's SAXParser, .NET's XmlReader and the browser's DOMParser all describe the identical problem differently. The xml tag on Stack Overflow is the fastest way to work out which parser produced yours, and libxml2 is worth knowing by name because it sits underneath an enormous amount of tooling, including Python's lxml, PHP's SimpleXML and the xmllint command line.
When you will reach for this
A SOAP or partner feed that arrives slightly wrong
Integration partners generate XML with string templates more often than anyone admits, and the result works right up until a customer name contains an ampersand or an apostrophe. You cannot fix their exporter this afternoon, but you can repair the payload, confirm the rest of the structure is sound, and send them a reproducible example. Once it parses, XML to Table will show you the records as rows so you can see which ones are actually affected.
Config files that stopped a service from starting
Tomcat's server.xml, Maven's pom.xml, an Android manifest, a Spring bean definition — all of them fail hard and early on a syntax error, and the stack trace usually buries the real line number under twenty frames. Repair the file here, then run it through the XML Formatter so the indentation matches what was there before and your diff stays reviewable.
XML pulled out of a log or a bug report
By the time a payload reaches a ticket it has usually been through a log formatter that wrapped the lines, a chat client that turned straight quotes into curly ones, and a copy that stopped at the edge of a terminal. Getting it back to something parseable is the first step; after that, XML to JSON often makes it far easier to read than the original ever was.
Scraped or hand-edited data on its way into a pipeline
Anything assembled with string concatenation eventually meets a value it cannot handle. The fixer will unblock you, but treat a repair as a signal rather than a solution — the real fix is a serializer that escapes for you. Once the document is clean, XML to CSV is a quick way to get it into a spreadsheet for a sanity check.
Payloads full of long identifiers
Telecom and banking XML is dense with 18- and 19-digit numbers: ICCIDs, IMSIs, IBANs, card PANs. Plenty of converters route every numeric-looking value through a floating point type and quietly turn 8901240544102066246 into 8901240544102066000. This page treats the repaired document as text from end to end, so nothing is ever re-typed behind your back.
Frequently asked questions
What is the difference between well-formed and valid XML?
Well-formed means the syntax is legal: one root element, every tag closed and correctly nested, attributes quoted, entities escaped. Valid means the document additionally conforms to a schema — a DTD, an XSD or a RELAX NG grammar — that says which elements may appear where and what their content must look like. This page produces well-formed XML. A document can be perfectly well-formed and still be rejected by your XSD because a required element is missing, and no syntax repair can invent that element for you.
Will it change my content as well as my markup?
Only where the content is what makes the document illegal. A bare & becomes & and a bare < becomes < — the text is unchanged, its encoding is not. Everything else in your text nodes and attribute values should come through byte for byte. A long numeric identifier is the easiest thing to spot-check: count the digits in the output panel and compare.
Does it handle namespaces?
An undeclared prefix is a well-formedness problem in namespace-aware parsers, so it is in scope, and the usual repair is to bind the prefix on the root element. What it will not do is guess a meaningful namespace URI for you — if the original was urn:telco:sim and that string is nowhere in the document, no tool can recover it. If the prefix matters downstream, paste the declaration back in yourself before running the repair.
Can it fix a document that was cut off partway through?
It can make the fragment parse by closing everything that is still open, which is often exactly what you want when you only need to inspect the part you have. What it cannot do is recover the records that were never in the paste. If a file was truncated at a byte limit, go back to the source rather than trusting a completed-looking tree.
Why do I get "Extra content at the end of the document" when the file looks fine?
Almost always two root elements. Something appended a second top-level element — a loop that wrote one record per iteration without a wrapper, or two files concatenated. The parser reads the first complete tree, reaches what should be end of file, and finds more markup. Wrapping everything in a single container element fixes it, and that is what the repair does here.
Is HTML the same thing as XML?
No, and assuming so is behind a large share of the errors on this page. HTML parsers are built to recover from mistakes: unclosed <p> tags, unquoted attributes, mismatched case, stray & characters all get patched up silently by the browser. XML parsers are required to fail instead. XHTML was the attempt to make HTML obey XML rules, and DOMParser in the browser still lets you choose between the two modes — pass text/xml and you get strict behaviour with a parsererror element on failure, pass text/html and you get the forgiving one.
Is there a size limit?
Nothing hard-coded, but repairing a multi-megabyte document is slow and the result is much harder to review than it is worth. With a large feed it is usually quicker to find the one bad record — the validator gives you a line and column — and repair that fragment on its own before dropping it back in.
How do I stop this happening in the first place?
Never build XML by concatenating strings. Every language has a serializer that escapes for you: lxml or xml.etree in Python, XMLStreamWriter in Java, XmlWriter in .NET, DOMDocument in PHP. They handle the five predefined entities, attribute quoting and encoding declarations without you thinking about it. Add xmllint --noout yourfile.xml to CI and a malformed document never reaches a consumer.