{"record":{"id":"92fb32dac8997571","repo":"siyuan-note/siyuan","slug":"svg-contains-an-unexpected-closing-element","errorCode":null,"errorMessage":"svg contains an unexpected closing element","messagePattern":"svg contains an unexpected closing element","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/util/misc.go","lineNumber":398,"sourceCode":"\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)\n\t\t\ttyped.Attr = sanitizeSVGAttributes(typed.Attr)\n\t\t\tif err = encoder.EncodeToken(typed); err != nil {\n\t\t\t\treturn \"\", fmt.Errorf(\"render svg failed: %w\", err)\n\t\t\t}\n\t\tcase xml.EndElement:\n\t\t\tif depth <= 0 || len(elementStack) == 0 {\n\t\t\t\treturn \"\", fmt.Errorf(\"svg contains an unexpected closing element\")\n\t\t\t}\n\t\t\tstartName := elementStack[len(elementStack)-1]\n\t\t\tif startName != typed.Name {\n\t\t\t\treturn \"\", fmt.Errorf(\"svg closing element %q does not match %q\", typed.Name.Local, startName.Local)\n\t\t\t}\n\t\t\telementStack = elementStack[:len(elementStack)-1]\n\t\t\tif skipDepth > 0 {\n\t\t\t\tskipDepth--\n\t\t\t\tdepth--\n\t\t\t\tif depth == 0 {\n\t\t\t\t\trootClosed = true\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\ttyped.Name = preserveXMLName(typed.Name)\n\t\t\tif err = encoder.EncodeToken(typed); err != nil {\n\t\t\t\treturn \"\", fmt.Errorf(\"render svg failed: %w\", err)\n\t\t\t}","sourceCodeStart":380,"sourceCodeEnd":416,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/util/misc.go#L380-L416","documentation":"The decoder produced an EndElement when the element stack is empty or depth is already 0, meaning the input contains a closing tag with no matching open tag. Go's strict XML tokenizer surfaces these malformations and SanitizeSVG treats any unbalanced markup as invalid SVG.","triggerScenarios":"Input like '<svg></svg></svg>' or '</svg>' alone; a stray extra close tag at top level; text containing '</' sequences that parse as tags because the input is being fed as XML.","commonSituations":"Manual edits to SVG files deleting an element but leaving its close tag; truncated/corrupted pastes; string concatenation bugs that append an extra '</svg>'.","solutions":["Remove the extra/unmatched closing tag from the input","Re-generate the SVG rather than hand-editing it","Run the input through a strict XML parser first to confirm well-formedness","Check any template/concatenation code for duplicated closing tags"],"exampleFix":"// before\nSanitizeSVG(\"<svg><rect/></svg></svg>\") // unexpected closing element\n// after\nSanitizeSVG(\"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\"><rect/></svg>\")","handlingStrategy":"validation","validationCode":"func xmlWellFormed(input string) error {\n\tdec := xml.NewDecoder(strings.NewReader(input))\n\tdec.Strict = true\n\tdepth := 0\n\tfor {\n\t\ttok, err := dec.RawToken()\n\t\tif err == io.EOF { return nil }\n\t\tif err != nil { return err }\n\t\tswitch tok.(type) {\n\t\tcase xml.StartElement: depth++\n\t\tcase xml.EndElement:\n\t\t\tdepth--\n\t\t\tif depth < 0 { return errors.New(\"closing tag without matching open tag\") }\n\t\t}\n\t}\n}","typeGuard":null,"tryCatchPattern":"if err := xmlWellFormed(input); err != nil {\n\treturn fmt.Errorf(\"svg markup is unbalanced: %w\", err)\n}\nclean, err := util.SanitizeSVG(input)\nif err != nil { return err }","preventionTips":["Do not hand-edit closing tags; regenerate or use an XML formatter","Run xmllint --noout on icon assets in CI","Check string-concatenation code that assembles SVG for duplicated '</svg>'","Treat any '</' outside a tag context in user input as suspicious"],"tags":["svg","xml","malformed-input","validation"],"backgroundTag":"schema-validation-failed","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"}