{"record":{"id":"ababb5d3bc4dc006","repo":"siyuan-note/siyuan","slug":"svg-contains-multiple-root-elements","errorCode":null,"errorMessage":"svg contains multiple root elements","messagePattern":"svg contains multiple root elements","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/util/misc.go","lineNumber":373,"sourceCode":"\t\t\tbreak\n\t\t}\n\t\tif err != nil {\n\t\t\treturn \"\", fmt.Errorf(\"parse svg failed: %w\", err)\n\t\t}\n\t\ttokenCount++\n\t\tif tokenCount > maxSVGTokens {\n\t\t\treturn \"\", fmt.Errorf(\"svg contains too many tokens\")\n\t\t}\n\n\t\tswitch typed := token.(type) {\n\t\tcase xml.StartElement:\n\t\t\telementStack = append(elementStack, typed.Name)\n\t\t\tdepth++\n\t\t\tif depth > maxSVGDepth {\n\t\t\t\treturn \"\", fmt.Errorf(\"svg nesting depth exceeds %d\", maxSVGDepth)\n\t\t\t}\n\t\t\tif rootClosed {\n\t\t\t\treturn \"\", fmt.Errorf(\"svg contains multiple root elements\")\n\t\t\t}\n\t\t\tif !rootSeen {\n\t\t\t\tif !strings.EqualFold(typed.Name.Local, \"svg\") {\n\t\t\t\t\treturn \"\", fmt.Errorf(\"root element is not svg\")\n\t\t\t\t}\n\t\t\t\trootSeen = true\n\t\t\t}\n\n\t\t\tif skipDepth > 0 {\n\t\t\t\tskipDepth++\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif _, unsafe := unsafeSVGElements[strings.ToLower(typed.Name.Local)]; unsafe {\n\t\t\t\tskipDepth = 1\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\ttyped.Name = preserveXMLName(typed.Name)","sourceCodeStart":355,"sourceCodeEnd":391,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/util/misc.go#L355-L391","documentation":"After the root <svg> element has been closed (rootClosed), any further StartElement token triggers this error. Well-formed XML allows exactly one root element, so a second top-level element means the input is a document fragment, concatenated SVGs, or has stray content after the root. The sanitizer enforces single-root XML semantics to keep output a valid standalone SVG.","triggerScenarios":"Passing content to SanitizeSVG that contains two sibling top-level elements — e.g. '<svg>...</svg><svg>...</svg>', 'text after </svg>', an SVG pasted twice, or template concatenation of multiple icon files.","commonSituations":"Batch-exported icon sets concatenated into one file, copy/paste that grabbed two icons, HTML snippets where multiple inline SVGs were joined, or a build step appending trailing content after the closing tag.","solutions":["Ensure the input contains exactly one root <svg> element — split multiple icons into separate files/calls","Strip any content (text, comments-producing artifacts, extra elements) after the closing </svg>","Fix the template/packaging step that concatenates SVG documents; wrap multiple icons in distinct sanitized calls","If a sprite is needed, place icons as <symbol> children inside a single root <svg> instead of sibling roots"],"exampleFix":"// before\ncombined := svg1 + svg2 // two root elements\nout, err := util.SanitizeSVG(combined)\n// after\nout1, err1 := util.SanitizeSVG(svg1)\nout2, err2 := util.SanitizeSVG(svg2) // sanitize each document separately","handlingStrategy":"validation","validationCode":"if strings.Count(strings.TrimSpace(svg), \"</svg>\") > 1 { return errors.New(\"multiple root elements; split into separate SVG documents\") }","typeGuard":null,"tryCatchPattern":"out, err := util.SanitizeSVG(svg)\nif err != nil && strings.Contains(err.Error(), \"multiple root elements\") {\n    // split the input on '</svg>' boundaries and sanitize each part\n}","preventionTips":["Ensure each file/paste contains exactly one <svg> root","Strip trailing junk after </svg> before sanitizing","For icon sets, use <symbol> children inside one root instead of sibling roots","Fix build/packaging scripts that concatenate SVG documents"],"tags":["svg","xml-parsing","sanitization","input-validation"],"backgroundTag":"xml-parse-error","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}