{"record":{"id":"0e0106648461944a","repo":"siyuan-note/siyuan","slug":"encoded-style-entity-is-not-attached-to-an-attribu","errorCode":null,"errorMessage":"encoded style entity is not attached to an attribute view rich text span","messagePattern":"encoded style entity is not attached to an attribute view rich text span","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/av/value.go","lineNumber":1949,"sourceCode":"\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}\n\t\tstyle := node.IALAttr(\"style\")\n\t\tstyleProtected := strings.Contains(style, sentinel)\n\t\ttextProtected := ast.NodeTextMark == node.Type && strings.Contains(node.TextMarkTextContent, sentinel)\n\t\tinlineMathProtected := ast.NodeTextMark == node.Type &&\n\t\t\tstrings.Contains(node.TextMarkInlineMathContent, sentinel)\n\t\ttokens := string(node.Tokens)\n\t\ttokensProtected := strings.Contains(tokens, sentinel)\n\t\tif styleProtected {\n\t\t\tif !node.IsTextMarkType(\"text\") {\n\t\t\t\terr = fmt.Errorf(\"encoded style entity is not attached to an attribute view rich text span\")\n\t\t\t\treturn ast.WalkStop\n\t\t\t}\n\t\t\tif textProtected || inlineMathProtected || tokensProtected ||\n\t\t\t\t!markValueTextRichStyleProtections(style, sentinel, tokenIndexes, restored) {\n\t\t\t\terr = fmt.Errorf(\"invalid encoded style entity in attribute view rich text span\")\n\t\t\t\treturn ast.WalkStop\n\t\t\t}\n\t\t\tstyle = styleReplacer.Replace(style)\n\t\t\tnode.SetIALAttr(\"style\", style)\n\t\t\tif nil != node.Next && ast.NodeKramdownSpanIAL == node.Next.Type {\n\t\t\t\tnode.Next.Tokens = parse.IAL2Tokens(node.KramdownIAL)\n\t\t\t}\n\t\t\treturn ast.WalkContinue\n\t\t}\n\t\tif textProtected || inlineMathProtected {\n\t\t\tif !node.IsTextMarkType(\"code\") && !node.IsTextMarkType(\"inline-math\") {\n\t\t\t\terr = fmt.Errorf(\"invalid encoded style entity in attribute view rich text mark [%s]\", node.TextMarkType)\n\t\t\t\treturn ast.WalkStop","sourceCodeStart":1931,"sourceCodeEnd":1967,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/av/value.go#L1931-L1967","documentation":"While restoring protected HTML style entities, the walker found a node whose style IAL contains the protection sentinel, but that node is not a plain \"text\" text mark. Protected style entities are only valid on text spans; style sentinels on any other node type (e.g. blockquote, heading, code block IAL) cannot be safely restored. The walker stops and rejects the payload.","triggerScenarios":"Calling av.NormalizeValueTextRich / NormalizeRichContent on rich text content where the style attribute of a non-text node contains a protection token (sentinel + index + \\ue002), typically after content was edited by a plugin or older version that placed style sentinels outside text spans.","commonSituations":"Hand-written or plugin-generated Kramdown that attaches a styled IAL to a non-text node; data produced by a different SiYuan version whose entity-protection placement rules differed; corrupted .sy-derived attribute view values.","solutions":["Remove the sentinel/style-entity token from the style attribute of the offending non-text node","Move the protected style onto a text span (a node of text mark type \"text\") instead","Re-run normalization after cleaning the rich text content; if it came from an older format, re-save the attribute view value through the current client first"],"exampleFix":"// before (non-text node carries protected style)\n<span data-type=\"inline-code\" style=\"color: var(--b3-font-color8)\">x</span>\n// after (move style to a text span)\n<span data-type=\"text\" style=\"color: var(--b3-font-color8)\">x</span>","handlingStrategy":"validation","validationCode":"// before sending rich text: ensure style sentinels only appear on text spans\nfor (const node of walkTree(tree)) {\n  const style = node.ial?.style ?? \"\"\n  if (style.includes(\"\\ue000\") && node.type !== \"text\") {\n    throw new Error(`protected style on non-text node: ${node.type}`)\n  }\n}","typeGuard":"const isTextSpan = (node) => node.type === \"text\"","tryCatchPattern":"try {\n  await api.normalizeAvValueText(payload)\n} catch (e) {\n  if (String(e).includes(\"not attached to an attribute view rich text span\")) {\n    payload = stripStyleSentinelsFromNonTextNodes(payload)\n    await api.normalizeAvValueText(payload)\n  }\n}","preventionTips":["Only author styles on text spans inside attribute view rich text","Strip \\ue000-\\ue002 private-use characters from pasted or imported content","Re-save values through the current client after version upgrades"],"tags":["attribute-view","rich-text","kramdown","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-14T00:17:10.932Z"}