666ghj/MiroFish · error · StarHistoryError
generated SVG is not valid XML
Error message
generated SVG is not valid XML
What it means
Raised by `_validate_svg` (scripts/star_history.py:1087) when `xml.etree.ElementTree.fromstring` rejects the payload — the bytes decoded fine and contain no forbidden directives, but the document is not well-formed XML (unclosed tags, mismatched tags, stray `&`, malformed attributes).
Source
Thrown at scripts/star_history.py:1087
def _validate_svg(payload: bytes) -> None:
try:
decoded_payload = payload.decode("utf-8", errors="strict")
except UnicodeDecodeError as exc:
raise StarHistoryError("generated SVG must be strict UTF-8") from exc
if decoded_payload.startswith("\ufeff") or "\x00" in decoded_payload:
raise StarHistoryError("generated SVG must be canonical UTF-8")
upper_payload = decoded_payload.upper()
if (
"<!DOCTYPE" in upper_payload
or "<!ENTITY" in upper_payload
or "<?" in decoded_payload
):
raise StarHistoryError("generated SVG contains forbidden XML directives")
try:
root = ET.fromstring(decoded_payload)
except ET.ParseError as exc:
raise StarHistoryError("generated SVG is not valid XML") from exc
svg_namespace = "{http://www.w3.org/2000/svg}"
if root.tag != f"{svg_namespace}svg":
raise StarHistoryError("generated SVG root is invalid")
allowed_attributes: dict[str, set[str]] = {
"svg": {
"viewBox",
"width",
"height",
"preserveAspectRatio",
"role",
"aria-labelledby",
},
"title": {"id"},
"desc": {"id"},
"rect": {
"x",
"y",
"width",View on GitHub (pinned to b5b53acc57)
Solutions
- If you forked the text generation, escape all interpolated values: `xml.sax.saxutils.escape(value)` and `quoteattr(value)` for attributes.
- Test round-trip in CI: `render_svg(...)` then `ET.fromstring(payload)` immediately — catches escaping regressions at build time.
- If validating an external file, re-run the renderer instead of repairing the broken file; canonical output always parses.
- Locate the defect with `ET.fromstring` traceback line/column numbers, which point at the exact malformed construct.
Example fix
# before: raw interpolation breaks XML on '&' or '<'
title_text = f"{owner}/{name} Star History"
# after: escape dynamic text
from xml.sax.saxutils import escape
title_text = escape(f"{owner}/{name} Star History") Defensive patterns
Strategy: try-catch
Validate before calling
import xml.etree.ElementTree as ET
def is_well_formed_xml(payload: bytes) -> bool:
try:
ET.fromstring(payload)
return True
except ET.ParseError:
return False Try / catch
try:
_validate_svg(payload)
except StarHistoryError as exc:
if "not valid XML" in str(exc):
raise ValueError(f"SVG is malformed: {exc.__cause__}") from exc
raise Prevention
- Escape all dynamic text with xml.sax.saxutils.escape / quoteattr in forked templates.
- Add a CI step that parses render_svg output immediately after generation.
- Never assemble SVG by f-string concatenation of untrusted values.
When it happens
Trigger: Any ET.ParseError while parsing the decoded SVG: unescaped `&` in text (e.g. 'AT&T'), truncated output from a bad write, hand-string-concatenated fragments, or fork code that builds tags via f-strings without escaping.
Common situations: Forks that interpolate repository names or usernames containing `<`, `>`, `&`, or quotes directly into SVG text; partial writes from crashed processes; templates split across concatenation points that lose a closing tag.
Related errors
- generated SVG contains forbidden XML directives
- generated SVG root is invalid
- generated SVG contains a foreign namespace
- generated SVG must be strict UTF-8
- generated SVG must be canonical UTF-8
AI-assisted analysis of 666ghj/MiroFish@b5b53acc57 (2026-08-14).
Data as JSON: /api/errors/fcdae89cb87f6b5e.
Report an issue: GitHub.