siyuan-note/siyuan · error
parse svg failed
Error message
parse svg failed: %w
What it means
SanitizeSVG parses SVG input with encoding/xml in strict mode using RawToken. Any XML parsing error (malformed markup, invalid entities, bad UTF-8, unexpected EOF) aborts sanitization and is wrapped in this error. The sanitizer intentionally refuses to guess at malformed XML, because HTML/XML parsing differences are the classic vector for SVG XSS bypasses.
Solutions
- Validate the SVG with an XML parser (e.g. xmllint --noout file.svg) and fix the reported syntax error
- Ensure the file is complete and UTF-8 encoded, not truncated in transfer
- If the source is HTML-ish SVG, convert it to well-formed XML (self-close tags, escape & as &)
- Re-export the SVG from the design tool with XML-compliant output options
Example fix
// before (invalid XML) <svg><desc>a & b</desc></svg> // after <svg><desc>a & b</desc></svg>
Defensive patterns
Strategy: validation
Validate before calling
func isWellFormedXML(svg string) bool {
d := xml.NewDecoder(strings.NewReader(svg))
d.Strict = true
for {
_, err := d.RawToken()
if err == io.EOF { return true }
if err != nil { return false }
}
} Try / catch
clean, err := util.SanitizeSVG(input)
if err != nil && strings.HasPrefix(err.Error(), "parse svg failed") {
return fmt.Errorf("input is not well-formed XML, re-export the SVG: %w", err)
} Prevention
- Pre-validate user SVG uploads with a strict XML parser
- Reject HTML-flavored markup at ingestion time
- Ensure files are complete and UTF-8 before sanitizing
- Never hand-edit SVG tags; re-export from the tool
When it happens
Trigger: Calling SanitizeSVG (directly, via custom emoji normalization, or the serveSVG HTTP path) with input that is not well-formed XML: unclosed tags, stray '<' or '&' characters, invalid entity references, non-UTF-8 bytes, or truncated files.
Common situations: Users paste SVG copied from HTML pages (HTML-tolerant markup that isn't valid XML), corrupted downloaded SVG files, SVG exported by tools that emit non-XML syntax, or clipper-captured fragments cut mid-tag.
Related errors
- svg contains an unexpected closing element
- render svg failed
- svg closing element does not match
- svg contains multiple root elements
- svg contains text outside the root element
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/aee9595fac752222.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/util/misc.go:358
decoder := xml.NewDecoder(strings.NewReader(svgInput))
decoder.Strict = true
var buf bytes.Buffer
encoder := xml.NewEncoder(&buf)
rootSeen := false
rootClosed := false
depth := 0
skipDepth := 0
tokenCount := 0
var elementStack []xml.Name
for {
token, err := decoder.RawToken()
if err == io.EOF {
break
}
if err != nil {
return "", fmt.Errorf("parse svg failed: %w", err)
}
tokenCount++
if tokenCount > maxSVGTokens {
return "", fmt.Errorf("svg contains too many tokens")
}
switch typed := token.(type) {
case xml.StartElement:
elementStack = append(elementStack, typed.Name)
depth++
if depth > maxSVGDepth {
return "", fmt.Errorf("svg nesting depth exceeds %d", maxSVGDepth)
}
if rootClosed {
return "", fmt.Errorf("svg contains multiple root elements")
}
if !rootSeen {
if !strings.EqualFold(typed.Name.Local, "svg") {View on GitHub (pinned to 9f775e8a12)