siyuan-note/siyuan · error

attribute view rich text style entity was not restored

Error message

attribute view rich text style entity was not restored

What it means

After walking the tree and applying all replacements, at least one protection entry was never marked as restored: its token never appeared (in a valid form) anywhere in the tree. Every protection created during the protect phase must be consumed exactly once; an unconsumed token means content was lost or the tree does not correspond to the protection set.

Source

Thrown at kernel/av/value.go:2001

		switch node.Type {
		case ast.NodeCodeBlockCode, ast.NodeMathBlockContent, ast.NodeCodeSpanContent, ast.NodeInlineMathContent:
			if !markValueTextRichStyleProtections(tokens, sentinel, tokenIndexes, restored) {
				err = fmt.Errorf("invalid encoded style entity in attribute view rich text node [%s]", node.Type.String())
				return ast.WalkStop
			}
			node.Tokens = []byte(literalReplacer.Replace(tokens))
		default:
			err = fmt.Errorf("invalid encoded style entity in attribute view rich text node [%s]", node.Type.String())
			return ast.WalkStop
		}
		return ast.WalkContinue
	})
	if nil != err {
		return
	}
	for _, found := range restored {
		if !found {
			return fmt.Errorf("attribute view rich text style entity was not restored")
		}
	}
	ast.Walk(tree.Root, func(node *ast.Node, entering bool) ast.WalkStatus {
		if entering && valueTextRichNodeContainsSentinel(node, sentinel) {
			err = fmt.Errorf("attribute view rich text style entity sentinel was not removed from node [%s]",
				node.Type.String())
			return ast.WalkStop
		}
		return ast.WalkContinue
	})
	return
}

func valueTextRichNodeContainsSentinel(node *ast.Node, sentinel string) bool {
	values := []string{
		node.ID, node.Spec, node.Data, string(node.Tokens), string(node.CodeBlockOpenFence),
		string(node.CodeBlockInfo), string(node.CodeBlockCloseFence), string(node.LinkRefLabel),
		string(node.FootnotesRefLabel), node.FootnotesRefId, string(node.HtmlEntityTokens),

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Inspect the rich text for styled spans that the Kramdown parser drops or rewrites (malformed HTML, unsupported attributes) and fix them
  2. Re-author the value so protected entities live in constructs that survive a parse/render round trip
  3. Verify the protection set matches the tree actually being restored (same pass, same content)
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-check: styled spans must be constructs the Kramdown parser preserves
for (const span of styledSpans(tree)) {
  if (!/^<span\s+[^>]*style=/.test(span.raw)) {
    throw new Error("styled content uses a construct that may not round-trip")
  }
}

Try / catch

try {
  await api.normalizeAvValueText(payload)
} catch (e) {
  if (String(e).includes("style entity was not restored")) {
    payload = rebuildRichTextWithoutDroppedSpans(payload)
    await api.normalizeAvValueText(payload)
  }
}

Prevention

When it happens

Trigger: Normalizing rich text where the protect pass created tokens for style entities but a later parse/render step dropped or transformed the containing node (e.g. Lute normalized away the styled span), leaving the token unused.

Common situations: Style entities inside constructs that get rewritten during Kramdown round-tripping (e.g. invalid HTML that the parser discards); content where a styled span was removed by normalization before restoration ran.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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