{"record":{"id":"6a0385f11c45057d","repo":"siyuan-note/siyuan","slug":"active-tab-s-is-not-a-direct-child-of-s","errorCode":null,"errorMessage":"active tab [%s] is not a direct child of [%s]","messagePattern":"active tab \\[(.+?)\\] is not a direct child of \\[(.+?)\\]","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/treenode/tabs.go","lineNumber":69,"sourceCode":"\t\tvalue = strings.TrimSpace(util.RemoveInvalidRetainCtrl(value))\n\t\tif \"\" == value {\n\t\t\tcontinue\n\t\t}\n\t\tif TabsPositionAttr == name {\n\t\t\tif \"top\" != value && \"left\" != value {\n\t\t\t\treturn fmt.Errorf(\"invalid tabs position [%s]\", value)\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tfound := false\n\t\tfor item := node.FirstChild; nil != item; item = item.Next {\n\t\t\tif ast.NodeTabItem == item.Type && item.ID == value {\n\t\t\t\tfound = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif !found {\n\t\t\treturn fmt.Errorf(\"active tab [%s] is not a direct child of [%s]\", value, node.ID)\n\t\t}\n\t}\n\treturn nil\n}\n\n// NormalizeTabs 修复空页和无效选中项，保留所有已有内容及块标识。\nfunc NormalizeTabs(root *ast.Node) (changed bool) {\n\tif nil == root {\n\t\treturn\n\t}\n\tast.Walk(root, func(node *ast.Node, entering bool) ast.WalkStatus {\n\t\tif entering {\n\t\t\treturn ast.WalkContinue\n\t\t}\n\t\tif ast.NodeTabItem == node.Type {\n\t\t\tif !hasContentChild(node) {\n\t\t\t\tnode.AppendChild(NewParagraph(\"\"))\n\t\t\t\tchanged = true","sourceCodeStart":51,"sourceCodeEnd":87,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/treenode/tabs.go#L51-L87","documentation":"When the tabs active-id attribute is set on a tab container, ValidateTabsAttrs walks the node's direct children looking for a NodeTabItem whose ID equals the given value. If no direct child matches, the attribute would point at a tab that is not an immediate child, so the update is rejected.","triggerScenarios":"Setting the tabs active-id attribute via setNodeAttrs0 to an ID that is not a direct NodeTabItem child of the target tabs node — e.g. a nested block inside a tab panel, a tab from another tabs container, or a stale/deleted tab ID.","commonSituations":"Plugins caching tab IDs that were later deleted or moved; confusing the content block inside a tab with the tab item itself; referencing tabs from a different container after refactoring.","solutions":["Use the ID of a direct NodeTabItem child of the tabs container (not the content block inside it)","Re-read the tabs container's children to get current, valid tab IDs before setting the attribute","Remove the attribute or let NormalizeTabs repair the invalid selection if the tab was deleted"],"exampleFix":"// before\nsetNodeAttrs(tabsID, { \"tabs-active-id\": someDeepBlockID });\n// after: pick an actual direct child tab item\nconst tabItemID = directChildTabItems(tabsID)[0].id;\nsetNodeAttrs(tabsID, { \"tabs-active-id\": tabItemID });","handlingStrategy":"validation","validationCode":"const tabsNode = await getBlockByID(tabsID);\nconst childIDs = await getChildBlockIDs(tabsID); // direct children only\nif (!childIDs.includes(activeTabID)) {\n  throw new Error(`${activeTabID} is not a direct child of ${tabsID}`);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Re-read tab item IDs from the live tree; never cache them across edits","Distinguish tab item IDs from the content block IDs inside each tab","Let NormalizeTabs repair invalid selections after deletions"],"tags":["tabs","parent-child","validation"],"backgroundTag":"entity-not-found","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"}