{"record":{"id":"2a0adfb5712cd9c9","repo":"siyuan-note/siyuan","slug":"invalid-tabs-position-s","errorCode":null,"errorMessage":"invalid tabs position [%s]","messagePattern":"invalid tabs position \\[(.+?)\\]","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/treenode/tabs.go","lineNumber":57,"sourceCode":"}\n\n// ValidateTabsAttrs 在修改属性前校验页签属性，避免部分属性已写入后才发现错误。\nfunc ValidateTabsAttrs(node *ast.Node, attrs map[string]string) error {\n\tfor name, value := range attrs {\n\t\tname = strings.ToLower(name)\n\t\tif TabsActiveIDAttr != name && TabsPositionAttr != name {\n\t\t\tcontinue\n\t\t}\n\t\tif nil == node || ast.NodeTabs != node.Type {\n\t\t\treturn fmt.Errorf(\"attribute [%s] is only supported on tab containers\", name)\n\t\t}\n\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 修复空页和无效选中项，保留所有已有内容及块标识。","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/treenode/tabs.go#L39-L75","documentation":"When the tabs-position attribute is present on a tab container, ValidateTabsAttrs requires its trimmed value to be exactly \"top\" or \"left\". Any other string (after trimming whitespace and removing invalid control characters) is rejected with this error.","triggerScenarios":"Setting tabs-position on a NodeTabs node via setNodeAttrs0 with a value other than \"top\"/\"left\" — e.g. \"right\", \"bottom\", \"Top\", \"top \". with a non-breaking space, or an empty-but-nonblank leftover string.","commonSituations":"Hand-written plugin config offering more orientations than supported; case-sensitivity mistakes; copying values from CSS-like configs that allow \"bottom\"/\"right\".","solutions":["Set the value to exactly \"top\" or \"left\" (lowercase, no extra whitespace)","Validate/normalize user input in the plugin before calling setNodeAttrs","Omit the attribute entirely to keep the default position"],"exampleFix":"// before\nsetNodeAttrs(tabsID, { \"tabs-position\": \"Bottom\" });\n// after\nconst pos = value.trim().toLowerCase();\nif (pos === \"top\" || pos === \"left\") {\n  setNodeAttrs(tabsID, { \"tabs-position\": pos });\n}","handlingStrategy":"validation","validationCode":"const VALID = [\"top\", \"left\"];\nconst pos = String(value ?? \"\").trim().toLowerCase();\nif (!VALID.includes(pos)) {\n  throw new Error(`tabs-position must be \"top\" or \"left\", got ${value}`);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Whitelist positions in plugin settings UI instead of free text","Normalize case/whitespace before calling setNodeAttrs","Remember only \"top\" and \"left\" exist — no right/bottom"],"tags":["tabs","enum-value","validation"],"backgroundTag":"invalid-enum-value","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"}