{"record":{"id":"608a4ef29d00df46","repo":"siyuan-note/siyuan","slug":"invalid-heading-fold-scope","errorCode":null,"errorMessage":"invalid heading fold scope","messagePattern":"invalid heading fold scope","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/heading.go","lineNumber":61,"sourceCode":"\n\theading := treenode.GetNodeInTree(tree, id)\n\ttransaction = &Transaction{}\n\tif nil == heading || ast.NodeHeading != heading.Type {\n\t\treturn\n\t}\n\n\tvar headings []*ast.Node\n\tswitch scope {\n\tcase \"children\":\n\t\tif treenode.IsSelfFolded(heading) {\n\t\t\theadings = []*ast.Node{heading}\n\t\t} else {\n\t\t\theadings = treenode.HeadingDirectChildren(heading)\n\t\t}\n\tcase \"siblings\":\n\t\theadings = treenode.HeadingSiblings(heading)\n\tdefault:\n\t\terr = errors.New(\"invalid heading fold scope\")\n\t\treturn\n\t}\n\ttransaction = buildHeadingFoldTransaction(headings)\n\treturn\n}\n\nfunc buildHeadingFoldTransaction(headings []*ast.Node) (transaction *Transaction) {\n\ttransaction = &Transaction{}\n\tfoldAll := false\n\tfor _, heading := range headings {\n\t\tif !treenode.IsSelfFolded(heading) {\n\t\t\tfoldAll = true\n\t\t\tbreak\n\t\t}\n\t}\n\n\tfor i := len(headings) - 1; 0 <= i; i-- {\n\t\theading := headings[i]","sourceCodeStart":43,"sourceCodeEnd":79,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/heading.go#L43-L79","documentation":"GetHeadingFoldTransaction folds a heading according to a scope (children/direct children/siblings). If the scope string is anything other than the recognized values, the switch falls to default and returns 'invalid heading fold scope'. The recognized scopes are the literal cases in the switch, e.g. 'children', 'directChildren', 'siblings'.","triggerScenarios":"Calling GetHeadingFoldTransaction (HTTP /api/transactions getHeadingFoldTransaction) with a fold action whose scope parameter is misspelled, empty, or in a different casing than the kernel expects.","commonSituations":"Plugin authors guessing scope names; API clients written against outdated docs; camelCase vs concatenated casing mismatches such as 'direct_children' vs 'directChildren'.","solutions":["Use exactly one of the supported scope strings as implemented in the switch ('children', 'directChildren', 'siblings')","Check the transaction payload sent by the client and log the actual scope value","Update the client to a version matching the kernel API"],"exampleFix":"// before\nconst action = {action: 'foldHeading', id: headingID, scope: 'subtree'};\n// after\nconst action = {action: 'foldHeading', id: headingID, scope: 'children'}; // 'children' | 'directChildren' | 'siblings'","handlingStrategy":"validation","validationCode":"const SCOPES = new Set(['children', 'directChildren', 'siblings']);\nif (!SCOPES.has(scope)) throw new Error('invalid heading fold scope: ' + scope);","typeGuard":"const isFoldScope = (s) => ['children', 'directChildren', 'siblings'].includes(s);","tryCatchPattern":"try {\n  await sendTransaction(action);\n} catch (e) {\n  if (String(e.msg).includes('invalid heading fold scope')) {\n    console.error('scope must be children | directChildren | siblings');\n  } else throw e;\n}","preventionTips":["Keep the scope strings in a shared constant instead of inlining literals","Match the casing used by the kernel exactly","Test fold transactions with each supported scope once"],"tags":["fold","validation","enum"],"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"}