ExpatError / ParseError: no element found: line 1, column 0 (tested)
xml.parsers.expat.ExpatError: no element found: line 1, column 0 (or xml.etree.ElementTree.ParseError with the same text) means the XML ended before a complete root element. We reproduced every common cause and found the position is the key clue: line 1, column 0 means the parser received zero bytes -- usually an empty file or response, or a file object that was already read to the end -- while a later position means the document was truncated or had no root element.
1. Every common cause, measured
import io, os, tempfile
import xml.etree.ElementTree as ET
import xml.dom.minidom, xml.parsers.expat
def try_parse(label, fn):
try:
fn()
print(f"{label:38} OK")
except Exception as e:
print(f"{label:38} {type(e).__module__}.{type(e).__name__}: {e}")
path = os.path.join(tempfile.mkdtemp(), "data.xml")
open(path, "w").close() # empty file
try_parse("ET.parse(empty file)", lambda: ET.parse(path))
try_parse("minidom.parse(empty file)", lambda: xml.dom.minidom.parse(path))
try_parse("ET.fromstring('')", lambda: ET.fromstring(""))
try_parse("ET.fromstring(whitespace only)", lambda: ET.fromstring(" \n "))
try_parse("minidom.parseString('')", lambda: xml.dom.minidom.parseString(""))
try_parse("ET.fromstring(declaration only)", lambda: ET.fromstring('<?xml version="1.0"?>\n'))
try_parse("ET.fromstring(comment only)", lambda: ET.fromstring("<!-- nothing -->"))
with open(path, "w") as f:
f.write("<root><item>1</item></root>")
with open(path) as f:
f.read() # something already read it
try_parse("ET.parse(file already read)", lambda: ET.parse(f))
f.seek(0)
try_parse("ET.parse(after f.seek(0))", lambda: ET.parse(f))
buf = io.StringIO()
buf.write("<root/>") # written, not rewound
try_parse("ET.parse(StringIO, no seek)", lambda: ET.parse(buf))
buf.seek(0)
try_parse("ET.parse(StringIO, after seek(0))", lambda: ET.parse(buf))
try_parse("ET.fromstring(truncated)", lambda: ET.fromstring("<root><item>1</item>"))
ET.parse(empty file) xml.etree.ElementTree.ParseError: no element found: line 1, column 0
minidom.parse(empty file) xml.parsers.expat.ExpatError: no element found: line 1, column 0
ET.fromstring('') xml.etree.ElementTree.ParseError: no element found: line 1, column 0
ET.fromstring(whitespace only) xml.etree.ElementTree.ParseError: no element found: line 2, column 1
minidom.parseString('') xml.parsers.expat.ExpatError: no element found: line 1, column 0
ET.fromstring(declaration only) xml.etree.ElementTree.ParseError: no element found: line 2, column 0
ET.fromstring(comment only) xml.etree.ElementTree.ParseError: no element found: line 1, column 16
ET.parse(file already read) xml.etree.ElementTree.ParseError: no element found: line 1, column 0
ET.parse(after f.seek(0)) OK
ET.parse(StringIO, no seek) xml.etree.ElementTree.ParseError: no element found: line 1, column 0
ET.parse(StringIO, after seek(0)) OK
ET.fromstring(truncated) xml.etree.ElementTree.ParseError: no element found: line 1, column 20
2. Read the line and column: they tell you which cause it is
| Position in the error | What it means | Typical cause |
|---|---|---|
line 1, column 0 | The parser received zero bytes | Empty file; empty string or b'' HTTP body; a file object already read to the end; a StringIO/BytesIO you wrote to and never rewound |
| Further along, at the end of the input | Input ended before any root element started, or before it was closed | Whitespace only; only an <?xml ...?> declaration; only a comment; a truncated document (<root><item>1</item> fails at column 20, its end) |
Measured: xml.etree.ElementTree raises ParseError and xml.dom.minidom raises xml.parsers.expat.ExpatError, both with the same expat message -- the module name differs, the meaning doesn't. "no element found" never means your XML has a syntax error in the middle; it means the document ended before a complete root element.
3. Fixes
- File already read / buffer not rewound: call
f.seek(0)(orbuf.seek(0)) before parsing. Measured above: the same handle parses fine afterseek(0). - Empty file or empty response: check before parsing --
if not data.strip(): ...-- and log where the empty input came from. For HTTP, checkr.status_codeandr.contentfirst: a 204, a redirect you didn't follow, or an error page served as an empty body all end up here. - Truncated document: the source was cut off (interrupted download, a writer that hadn't flushed/closed the file yet, a size-limited read). Fix the producer, or for an HTTP body see our IncompleteRead guide.
Related: ElementTree.parse() on a non-blocking stream and xmlrpc.client ServerProxy ExpatError on a valid reply, two less obvious ways to get zero bytes.
Frequently asked questions
What does 'no element found: line 1, column 0' mean?
The parser got no input at all. We measured it for an empty file, an empty string, b'', a file object that had already been read, and a StringIO that was written to without seek(0).
Why does it fail even though the file has valid XML?
Most often because something already read the file object (a debug print(f.read()), a size check, an earlier parse) and the position is at the end. Call f.seek(0) before parsing, or reopen the file.
Is ParseError different from ExpatError?
ElementTree raises xml.etree.ElementTree.ParseError and minidom raises xml.parsers.expat.ExpatError, but both come from the same expat parser with the same message. Catch the one for the module you use.
Does an XML declaration or comment count as an element?
No. We measured that input containing only <?xml version="1.0"?> or only a comment still raises 'no element found' -- a document needs one root element.
I get it when parsing requests' r.content. Why?
r.content is b'' -- ET.fromstring(b'') raises the same 'no element found: line 1, column 0'. Check r.status_code and the headers first; the server sent an empty body.
Written with AI assistance. Measured on 2026-10-09 with Python 3.13.5's built-in xml.etree.ElementTree, xml.dom.minidom and xml.parsers.expat, parsing empty, whitespace-only, declaration-only, comment-only and truncated input, plus file and StringIO objects that were already at end-of-file. PlainHeaders is our extension; Python's XML modules are built in.