siyuan-note/siyuan · error

attribute view rich text style entity sentinel was not remov

Error message

attribute view rich text style entity sentinel was not removed from node [%s]

What it means

After all protection tokens were restored, a final sweep (valueTextRichNodeContainsSentinel) still found the sentinel string somewhere in a node — its tokens, text mark fields, IAL attributes, or properties. Residual sentinel text means the cleanup pass missed a location, and leaving the private-use sentinel characters in the saved value would corrupt future normalization runs.

Source

Thrown at kernel/av/value.go:2006

			}
			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),
		node.TextMarkType, node.TextMarkAHref, node.TextMarkATitle, node.TextMarkInlineMathContent,
		node.TextMarkInlineMemoContent, node.TextMarkBlockRefID, node.TextMarkBlockRefSubtype,
		node.TextMarkFileAnnotationRefID, node.TextMarkFlashcardOcclusionID, node.TextMarkTextContent,
	}
	for _, value := range values {

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Locate the node type reported in the message and remove the residual sentinel characters from that field
  2. Strip \ue000-\ue002 private-use characters from user-supplied content before feeding it to the attribute view API
  3. Re-run normalization on the cleaned value to confirm the sentinel sweep passes
Defensive patterns

Strategy: validation

Validate before calling

// strip private-use sentinel characters from all string fields before sending
function scrub(obj) {
  for (const k in obj) {
    if (typeof obj[k] === "string") obj[k] = obj[k].replace(/[\ue000-\ue002]/g, "")
    else if (obj[k] && typeof obj[k] === "object") scrub(obj[k])
  }
  return obj
}
payload = scrub(payload)

Type guard

const hasSentinel = (s) => typeof s === "string" && /[\ue000-\ue002]/.test(s)

Try / catch

try {
  await api.normalizeAvValueText(payload)
} catch (e) {
  if (String(e).includes("sentinel was not removed")) {
    payload = scrub(payload)
    await api.normalizeAvValueText(payload)
  }
}

Prevention

When it happens

Trigger: Normalizing rich text where sentinel characters appear in a node field not covered by the replacement replacers (e.g. link href/title, memo, block ref fields, node properties) — typically because user content itself contained the sentinel sequence or a token was copied into an unhandled field.

Common situations: Pasting content that already contains \ue000/\ue002 private-use characters; a plugin writing sentinel-like strings into attributes such as memo or title; a restoration pass where one replacer variant did not cover a field the token ended up in.

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