siyuan-note/siyuan · error

svg root element is incomplete

Error message

svg root element is incomplete

What it means

After consuming all tokens, SanitizeSVG requires: a root was seen, it was closed, depth returned to 0, no unsafe-subtree skip was in progress, and the element stack is empty. If any of these invariants fail — truncated input, unclosed unsafe element, or the root never closed — the input is not a complete SVG document and is rejected.

Source

Thrown at kernel/util/misc.go:450

			}
		case xml.Comment:
			if skipDepth == 0 && rootSeen && !rootClosed {
				if err = encoder.EncodeToken(typed); err != nil {
					return "", fmt.Errorf("render svg failed: %w", err)
				}
			}
		case xml.Directive:
			if !isBenignSVGDoctype(string(typed)) {
				return "", fmt.Errorf("svg directives are not allowed")
			}
			// 良性 DOCTYPE 声明不写入输出,与 XML 声明(ProcInst)的处理方式一致,不影响浏览器渲染
		case xml.ProcInst:
			// XML 声明和处理指令不影响 SVG 图像内容,输出时统一省略。
		}
	}

	if !rootSeen || !rootClosed || depth != 0 || skipDepth != 0 || len(elementStack) != 0 {
		return "", fmt.Errorf("svg root element is incomplete")
	}
	if err := encoder.Close(); err != nil {
		return "", fmt.Errorf("render svg failed: %w", err)
	}
	return buf.String(), nil
}

func preserveXMLName(name xml.Name) xml.Name {
	if name.Space != "" {
		name.Local = name.Space + ":" + name.Local
		name.Space = ""
	}
	return name
}

// isBenignSVGDoctype 判断 DOCTYPE 是否仅为 svg 根元素的无内部子集声明。
// Go 的 xml.Directive 是 <! 与 > 之间的内容;内部子集 [...] 内可声明实体,存在 XXE 风险,一律拒绝。
func isBenignSVGDoctype(d string) bool {

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Provide the complete '<svg>...</svg>' document including all closing tags
  2. Check for truncation: compare input length/hash against the source file, or verify it ends with '</svg>' (modulo trailing whitespace)
  3. Ensure no element (especially filtered ones like <script>) is left unclosed, which leaves skipDepth non-zero
  4. If accepting fragments, wrap them in a complete svg root and close all elements before sanitizing

Example fix

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

Strategy: validation

Validate before calling

func svgDocComplete(input string) bool {
	s := strings.TrimSpace(input)
	return strings.HasPrefix(strings.ToLower(s), "<") && strings.HasSuffix(s, "</svg>")
}

Type guard

func isCompleteSVG(input string) bool {
	if !strings.Contains(strings.ToLower(input), "<svg") { return false }
	if !strings.Contains(strings.ToLower(input), "</svg>") { return false }
	dec := xml.NewDecoder(strings.NewReader(input))
	_, err := xmldecAll(dec) // any full-token loop that runs to io.EOF without error
	return err == nil
}

Try / catch

clean, err := util.SanitizeSVG(input)
if err != nil && strings.Contains(err.Error(), "incomplete") {
	return fmt.Errorf("svg upload appears truncated; re-upload the full file: %w", err)
}

Prevention

When it happens

Trigger: Input truncated mid-document: '<svg><rect/>' with no closing tags; a <script> or other unsafe element opened but its close tag cut off (skipDepth never returns to 0); input consisting only of text/comments with no elements at all (rootSeen=false).

Common situations: Paste cut off by a size-limited input field; network transfer truncation; string processing that dropped the tail of the file; partial fragments saved as icon files.

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/3d6483e9c460bd66. Report an issue: GitHub.