siyuan-note/siyuan · error

root element is not svg

Error message

root element is not svg

What it means

SanitizeSVG requires the first (root) XML element of the input to be an <svg> element. When the first StartElement token has a local name other than 'svg' (compared case-insensitively), the sanitizer refuses the input entirely because anything wrapped in a non-SVG root is not a valid standalone SVG image and could be an attempt to smuggle other content.

Source

Thrown at kernel/util/misc.go:377

		}
		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") {
					return "", fmt.Errorf("root element is not svg")
				}
				rootSeen = true
			}

			if skipDepth > 0 {
				skipDepth++
				continue
			}
			if _, unsafe := unsafeSVGElements[strings.ToLower(typed.Name.Local)]; unsafe {
				skipDepth = 1
				continue
			}

			typed.Name = preserveXMLName(typed.Name)
			typed.Attr = sanitizeSVGAttributes(typed.Attr)
			if err = encoder.EncodeToken(typed); err != nil {
				return "", fmt.Errorf("render svg failed: %w", err)
			}

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Make the root element of the input a plain <svg> element (namespace http://www.w3.org/2000/svg), e.g. wrap fragments: <svg xmlns="http://www.w3.org/2000/svg">...</svg>
  2. Verify no leading non-element markup that could be mis-parsed as an element; comments/procinsts are allowed before the root
  3. Check the namespace prefix of the root: with decoder.Strict=true an undeclared prefix makes Space!=Local=='svg'; declare xmlns:s="http://www.w3.org/2000/svg" so the local name resolves correctly
  4. Strip surrounding HTML/wrapper markup before calling SanitizeSVG

Example fix

// before
SanitizeSVG("<g><rect/></g>") // root element is not svg
// after
SanitizeSVG("<svg xmlns=\"http://www.w3.org/2000/svg\"><g><rect/></g></svg>")
Defensive patterns

Strategy: validation

Validate before calling

func looksLikeSVG(input string) bool {
	s := strings.TrimSpace(input)
	for strings.HasPrefix(s, "<?") { if i := strings.Index(s, "?>"); i >= 0 { s = strings.TrimSpace(s[i+2:]) } else { break } }
	return strings.HasPrefix(strings.ToLower(s), "<svg") || strings.HasPrefix(strings.ToLower(s), "<!doctype")
}

Type guard

func isSVGRoot(input string) bool {
	dec := xml.NewDecoder(strings.NewReader(input))
	for {
		tok, err := dec.RawToken()
		if err != nil { return false }
		switch t := tok.(type) {
		case xml.StartElement:
			return strings.EqualFold(t.Name.Local, "svg")
		case xml.Comment, xml.ProcInst, xml.Directive, xml.CharData:
			continue
		default:
			return false
		}
	}
}

Try / catch

clean, err := util.SanitizeSVG(input)
if err != nil {
	if strings.Contains(err.Error(), "root element is not svg") {
		input = wrapAsSVGDocument(input) // add <svg xmlns=...> wrapper
		clean, err = util.SanitizeSVG(input)
	}
	if err != nil { return fmt.Errorf("invalid svg icon: %w", err) }
}

Prevention

When it happens

Trigger: Calling SanitizeSVG with input whose first element is not <svg>: an HTML fragment, a <defs> or <g> fragment pasted from another file, a prefixed root like <s:svg> whose namespace prefix the strict decoder does not resolve, or any markup before the svg tag (the first element seen must be svg).

Common situations: Users pasting partial SVG snippets (inner content only) into custom emoji / dynamic icon fields; templates that wrap icons in wrapper elements; documents that begin with comments/DOCTYPE are fine, but documents that begin with another element (e.g. <html> or an <img> wrapper) are not.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11). Data as JSON: /api/errors/a8304a6159b8b69f. Report an issue: GitHub.