{"record":{"id":"fc2e994e84ff2808","repo":"siyuan-note/siyuan","slug":"inconsistent-attribute-view-rich-text-style-entity","errorCode":null,"errorMessage":"inconsistent attribute view rich text style entity sentinel","messagePattern":"inconsistent attribute view rich text style entity sentinel","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/av/value.go","lineNumber":1921,"sourceCode":"\t}\n\treturn builder.String(), protections\n}\n\nfunc restoreValueTextRichTreeStyleEntities(tree *parse.Tree,\n\tprotections []valueTextRichStyleEntityProtection) (err error) {\n\tif 1 > len(protections) {\n\t\treturn\n\t}\n\tsentinel := protections[0].sentinel\n\tstylePairs := make([]string, 0, len(protections)*2)\n\tliteralPairs := make([]string, 0, len(protections)*2)\n\ttextMarkCodePairs := make([]string, 0, len(protections)*2)\n\ttextMarkInlineMathPairs := make([]string, 0, len(protections)*2)\n\ttokenIndexes := make(map[string]int, len(protections))\n\trestored := make([]bool, len(protections))\n\tfor i, protection := range protections {\n\t\tif sentinel != protection.sentinel {\n\t\t\treturn fmt.Errorf(\"inconsistent attribute view rich text style entity sentinel\")\n\t\t}\n\t\ttokenIndexes[protection.token] = i\n\t\tstylePairs = append(stylePairs, protection.token, protection.decoded)\n\t\tliteralPairs = append(literalPairs, protection.token, protection.encoded)\n\t\tencoded := strings.ReplaceAll(protection.encoded, \"&\", \"&amp;\")\n\t\ttextMarkCodePairs = append(textMarkCodePairs, protection.token, encoded)\n\t\ttextMarkInlineMathPairs = append(textMarkInlineMathPairs, protection.token,\n\t\t\tstrings.ReplaceAll(encoded, \"&\", \"&amp;\"))\n\t}\n\tstyleReplacer := strings.NewReplacer(stylePairs...)\n\tliteralReplacer := strings.NewReplacer(literalPairs...)\n\ttextMarkCodeReplacer := strings.NewReplacer(textMarkCodePairs...)\n\ttextMarkInlineMathReplacer := strings.NewReplacer(textMarkInlineMathPairs...)\n\n\tast.Walk(tree.Root, func(node *ast.Node, entering bool) ast.WalkStatus {\n\t\tif !entering {\n\t\t\treturn ast.WalkContinue\n\t\t}","sourceCodeStart":1903,"sourceCodeEnd":1939,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/av/value.go#L1903-L1939","documentation":"During restoration of protected style entities in an attribute view rich text tree, every protection entry must carry the same sentinel marker that was generated when the entities were protected. restoreValueTextRichTreeStyleEntities derives the expected sentinel from protections[0] and rejects the batch if any later entry carries a different sentinel. This is an internal invariant: protections are always created together by protectValueTextRichStyleEntities, so a mismatch means the slice was assembled or mutated incorrectly.","triggerScenarios":"Calling restoreValueTextRichTreeStyleEntities (via the rich text normalization path, e.g. NormalizeValueTextRich on a ValueText with a rich payload) with a protections slice whose entries were produced by different protectValueTextRichStyleEntities invocations, or with a programmatically modified slice where one entry's sentinel field differs from the first entry's.","commonSituations":"Kernel-side code combining protection slices from two separate protect passes; tests or tooling that hand-construct valueTextRichStyleEntityProtection entries with mismatched sentinel strings; a partial refactor that re-protects part of a document under a new sentinel but restores with the old slice.","solutions":["Ensure all protections passed to restoreValueTextRichTreeStyleEntities come from a single protectValueTextRichStyleEntities run over the same content","Verify the sentinel field of every protection entry matches protections[0].sentinel before calling the restore function","If merging content, re-run protection and restoration as one unified pass instead of stitching slices"],"exampleFix":"// before\nprotections = append(oldProtections, newProtections...)\nrestoreValueTextRichTreeStyleEntities(tree, protections)\n// after\nallProtected, _ := protectValueTextRichStyleEntities(combinedStyle, sentinel, offset)\nrestoreValueTextRichTreeStyleEntities(tree, allProtected)","handlingStrategy":"validation","validationCode":"const sentinel0 = protections[0]?.sentinel\nif (!protections.every(p => p.sentinel === sentinel0)) {\n  throw new Error(\"mixed sentinel protection sets; re-run protection in a single pass\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always generate and restore protections within one function scope","Never concatenate protection slices from different protect passes"],"tags":["attribute-view","internal-invariant","rich-text"],"backgroundTag":"internal-invariant-violation","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"}