{"record":{"id":"ed43442b88f6c873","repo":"siyuan-note/siyuan","slug":"svg-closing-element-q-does-not-match-q","errorCode":null,"errorMessage":"svg closing element %q does not match %q","messagePattern":"svg closing element %q does not match %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/util/misc.go","lineNumber":402,"sourceCode":"\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}\n\t\t\tdepth--\n\t\t\tif depth == 0 {\n\t\t\t\trootClosed = true\n\t\t\t}","sourceCodeStart":384,"sourceCodeEnd":420,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/util/misc.go#L384-L420","documentation":"A closing tag's element name does not equal the name of the innermost open element, i.e. mismatched start/end tags such as <rect>...</circle>. Strict XML forbids this; the sanitizer keeps a stack of open element names and compares each EndElement against the top of the stack.","triggerScenarios":"'<svg><g></rect></g></svg>' — nested tags closed in the wrong order; HTML-style tag soup fed to an XML parser; typos in tag names between open and close.","commonSituations":"Copy-paste editing that reorders or deletes tags; HTML-generated SVG where a browser's tag-soup recovery hid the mismatch; minifiers or hand-merges that break tag pairing.","solutions":["Fix the mismatched close tag so it matches the innermost open element","Run the SVG through an XML linter/formatter (e.g. xmllint) which pinpoints the mismatch line","Re-export the icon from its design tool instead of hand-editing","Validate with xml.Unmarshal in Go before calling SanitizeSVG"],"exampleFix":"// before\nSanitizeSVG(\"<svg><g></rect></g></svg>\") // closing element \"rect\" does not match \"g\"\n// after\nSanitizeSVG(\"<svg xmlns=\\\"http://www.w3.org/2000/svg\\\"><g></g></svg>\")","handlingStrategy":"validation","validationCode":"func tagsBalanced(input string) error {\n\tvar stack []string\n\tdec := xml.NewDecoder(strings.NewReader(input))\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 t := tok.(type) {\n\t\tcase xml.StartElement: stack = append(stack, t.Name.Local)\n\t\tcase xml.EndElement:\n\t\t\tif len(stack) == 0 || stack[len(stack)-1] != t.Name.Local {\n\t\t\t\treturn fmt.Errorf(\"mismatched tag %q\", t.Name.Local)\n\t\t\t}\n\t\t\tstack = stack[:len(stack)-1]\n\t\t}\n\t}\n}","typeGuard":null,"tryCatchPattern":"clean, err := util.SanitizeSVG(input)\nif err != nil && strings.Contains(err.Error(), \"does not match\") {\n\tinput, err = reformatXML(input) // e.g. pipe through xmllint or gofmt-XML style formatter\n\tif err == nil { clean, err = util.SanitizeSVG(input) }\n}\nif err != nil { return err }","preventionTips":["Never mix HTML tag-soup habits with SVG; every tag must close in LIFO order","Use self-closing syntax <tag/> for empty elements to avoid pairing mistakes","Lint icon assets with xmllint in CI","Avoid manual merge edits across SVG versions"],"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"}