Fix Apple Health export.xml Parse Errors — The Complete Guide (iOS 16+ Update)
Apple's export.xml has been broken since iOS 16. The "ATTLIST: no name for Attribute" error crashes Python, R, Go, and xmllint. Your file might be 1.7 GB. Here's every fix — and the permanent workaround that skips XML entirely.
Quick answer: what's wrong with Apple Health export.xml?
Since iOS 16 (HealthKit Export Version 12), Apple's export.xml has contained malformed DTD declarations. The XML includes incomplete <!ELEMENT> tags — like <!ELEMENT RightEye EMPTY> — without matching <!ATTLIST> declarations. Strict XML parsers reject this at approximately line 156 with:
ATTLIST: no name for Attribute [68]
Or, in Python's xml.etree.ElementTree:
xml.etree.ElementTree.ParseError: syntax error: line 156, column 0
If you're using R's read_xml, the R package XML, xmllint, or any standards-compliant XML parser — they all fail with the same root cause: Apple shipped an XML file that doesn't conform to its own DTD.
The file can be 1.7 GB or larger. The DTD error is in the first 200 lines. You don't even get to your data.
Why this happens
When you go to Settings → Privacy → Health → Export All Health Data, iOS produces a single massive XML file containing every HealthKit sample your phone has ever recorded. The file starts with a DOCTYPE declaration that defines the schema. In iOS 16+, Apple added new eye, audio, and other health data types to the schema but the DTD declarations for these types are malformed.
This has been a known issue since iOS 16.0 (late 2022). Apple partially fixed it in iOS 16.2, but subsequent releases (iOS 17, iOS 18) have brought their own DTD quirks. As of iOS 18 and 19 in 2026, users still report parse failures with certain tools — the fixes are fragile and version-dependent.
The core problem is architectural: Apple's export is a raw HealthKit serialization dump, not a format designed for consumption. It includes every individual sample, every metadata field, and every HKObjectType identifier — and it dumps them all into one file that grows without bound.
Fix 1: Strip the DOCTYPE (fastest, works everywhere)
Since every parser fails on the DTD, the simplest fix is to remove it. The data — the actual <Record> elements — is valid XML. It's only the schema declaration at the top that's broken.
Use sed to strip everything from <!DOCTYPE to the first <Record> tag:
sed '1,/^ export-fixed.xml
Then parse export-fixed.xml as a rootless XML fragment. In Python using xml.etree.ElementTree:
import xml.etree.ElementTree as ET
tree = ET.iterparse("export-fixed.xml")
for event, elem in tree:
if elem.tag == "Record":
print(elem.attrib)
elem.clear()
Using iterparse with elem.clear() is critical — it keeps memory usage bounded so the 1.7 GB file doesn't blow up your RAM.
For xmllint users, skip DTD validation entirely:
xmllint --noout --loaddtd --valid export.xml 2>/dev/null || \
xmllint --noout --nodtdattr export.xml
Downside: Stripping the DOCTYPE means you lose the schema. You'll need to know the attribute names and types by heart — type, sourceName, sourceVersion, unit, creationDate, startDate, endDate, value. The file becomes self-describing only if you keep the DTD — but the DTD is broken. It's a trade-off either way.
Fix 2: Pre-process with a Python script
If you need the DTD for schema validation, fix it before parsing. The error is caused by empty ELEMENT declarations for new health types. You can patch the DTD by removing those lines:
import re
with open("export.xml", "r", encoding="utf-8") as f:
raw = f.read()
# Remove the incomplete DTD lines that cause the error
fixed = re.sub(
r'\s*',
'',
raw
)
with open("export-fixed.xml", "w", encoding="utf-8") as f:
f.write(fixed)
This targets the specific problematic element types added in iOS 16. If Apple adds more types in iOS 19+, you'll need to update the regex. The script works by reading the file into memory, which is fine if you have enough RAM for the 1.7 GB file — budget ~3-4 GB for the string copy overhead.
Fix 3: Use a tolerant parser
Some XML libraries can be configured to skip DTD validation. lxml with recover=True is the most reliable option:
from lxml import etree
parser = etree.XMLParser(recover=True, load_dtd=False)
tree = etree.parse("export.xml", parser)
# Now iterate records
root = tree.getroot()
for record in root.iter("Record"):
print(record.attrib)
For R users, the XML package can be configured to skip validation:
library(XML)
doc <- xmlParse("export.xml", validate = FALSE)
records <- getNodeSet(doc, "//Record")
Be warned: these tools will parse the file, but the resulting tree consumes memory proportional to the file size. A 1.7 GB export.xml may use 5-10 GB of RAM after parsing.
Fix 4: Use a dedicated conversion tool
Several open-source tools handle the DTD problem for you:
- dogsheep/healthkit-to-sqlite (243 ★) — Simon Willison's Python tool converts export.xml to SQLite. It has an open issue documenting the iOS 16 DTD error and includes a workaround that strips the problematic lines before parsing.
- the-momentum/apple-health-mcp-server (243 ★) — Their Python/DuckDB stack parses the XML and handles the DTD issue internally.
- rinoshiyo/apple-health-mcp-server — A dedicated PyPI package for parsing export.xml with DuckDB.
All of these require you to export the XML from your iPhone, transfer it to your computer (which can take 15+ minutes over AirDrop or USB for a multi-GB file), and then wait for the parsing step. The parsing time is proportional to the file size — expect 5-30 minutes for large exports.
The permanent fix: skip export.xml entirely
None of the fixes above solve the root problem: export.xml is a terrible format for AI agents. It's huge, slow to parse, fragile across iOS versions, and contains all raw samples when you almost always want aggregates, trends, and summaries.
The permanent fix is to not use export.xml at all. Instead, use an iOS app that reads HealthKit through Apple's native Swift APIs and writes a clean, structured export directly.
Health Export AI does exactly this. The iOS app reads HealthKit read-only and produces a compact JSON file (typically 2-5 MB, not 1.7 GB) containing all 190 Apple Health metrics with proper units, metadata, and date ranges. No XML, no DTD, no parsing step. The file is ready for your AI agent the moment it lands in iCloud Drive.
For developers who need to query the data with their own tools, the MCP server reads the same JSON file and exposes it through seven deterministic query tools. For casual users, the on-device AI chat answers questions in plain English with provenance cards showing exactly which numbers produced each answer.
Neither path ever touches export.xml.
Real comparison: A user with an Apple Watch for 3 years generates ~1.3 GB of export.xml. Health Export AI's JSON cache for the same user: ~3.2 MB. That's ~400x smaller. The MCP server reads it in under 100 ms.
Comparison: XML fixes vs native HealthKit export
| Approach | Setup time | File size | Parse time | Fragile? |
|---|---|---|---|---|
| export.xml + DTD strip | ~15 min | 500 MB - 2 GB | 5-30 min | Yes (iOS version) |
| export.xml + lxml recover | ~15 min | 500 MB - 2 GB | 5-30 min | Partial (memory) |
| healthkit-to-sqlite | ~15 min | 500 MB - 2 GB | 5-30 min | Yes |
| Health Export AI (JSON) | ~2 min | 2-5 MB | <100 ms | No |
What about AI agents specifically?
If your goal is to feed your Apple Health data into an LLM — Claude, ChatGPT, Cursor — then export.xml is the wrong format on every dimension:
- Too large. A 1.7 GB XML file can't fit in any model's context window. You'd need to pre-filter and aggregate before the agent sees any data.
- Wrong structure. LLMs prefer structured, typed data with clear keys and values. XML with nested MetadataEntry elements and long HKObjectType identifiers is the opposite of that.
- No built-in query layer. You can't ask an agent "compare my HRV this week to last week" against a raw XML dump. Every query requires a custom extraction step.
- Stale immediately. Once exported, the XML is a snapshot. Any new HealthKit data requires a full re-export.
That's why Health Export AI was built: to bypass the XML pipeline entirely. The iOS app reads HealthKit natively, writes clean JSON, and the MCP server gives agents deterministic tools to query the data on demand. No XML, no DTD, no parsing step in the critical path.
For the full setup, see Export Apple Health data to JSON for AI or Connect Apple Health to Claude via MCP.
Skip the XML nightmare. Get clean JSON today.
190 Apple Health metrics as structured JSON. No export.xml, no DTD errors, no 1.7 GB files. Works with any AI agent. Free 7-day trial.
Frequently asked questions
Why does Apple Health export.xml fail to parse after iOS 16?
iOS 16 introduced HealthKit Export Version 12, which added new DTD declarations for eye, audio, and other health data types with malformed formatting. The XML includes incomplete ELEMENT declarations without matching ATTLIST declarations, causing strict parsers to fail near line 156 with "ATTLIST: no name for Attribute".
How do I fix the Apple Health export.xml parse error?
There are several fixes: strip the DOCTYPE declaration with sed, pre-process the XML to remove invalid DTD lines, use lxml with recover=True, or use a dedicated tool like healthkit-to-sqlite. The permanent solution is to skip export.xml entirely and use an app that reads HealthKit natively and outputs clean JSON.
How big can Apple Health export.xml be?
export.xml can reach 1.7 GB or more for users with years of HealthKit data and an Apple Watch. This causes memory issues with most XML parsers — they load the entire DOM into RAM, requiring 5-10 GB for large exports. Stream-based parsing (iterparse) helps but adds complexity.
Does iOS 18 or iOS 19 fix the export.xml DTD error?
Partially. Each iOS version has tweaked the DTD, but reports of parse failures persist across iOS 17, 18, and 19. The root issue — that export.xml is a raw serialization dump, not a consumption format — hasn't been addressed. Workarounds are version-specific and fragile.
Can I use export.xml with AI agents like Claude or ChatGPT?
Not directly. XML is a poor format for LLMs — verbose, nested, with long identifiers. You'd need to pre-process, filter, aggregate, and convert to a structured format before an agent can use it. A JSON export from a native HealthKit app is a much better fit for AI workflows.