{"record":{"id":"cf06356717991a01","repo":"siyuan-note/siyuan","slug":"invalid-reorder-position-s","errorCode":null,"errorMessage":"invalid reorder position [%s]","messagePattern":"invalid reorder position \\[(.+?)\\]","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/file.go","lineNumber":2722,"sourceCode":"\t\tfileTreeSortLock.Unlock()\n\t\treturn ret, writeErr\n\t}\n\tfileTreeSortLock.Unlock()\n\n\tret.Changed = true\n\tret.Notebook = box.ID\n\tret.ParentPath = parentPath\n\tIncSync()\n\tpushFiletreeSortChanged(sortIDs)\n\treturn\n}\n\nfunc validateReorderArgs(sourceIDs []string, targetID, position string) error {\n\tif 1 > len(sourceIDs) {\n\t\treturn errors.New(\"source IDs must not be empty\")\n\t}\n\tif \"before\" != position && \"after\" != position {\n\t\treturn fmt.Errorf(\"invalid reorder position [%s]\", position)\n\t}\n\tseen := map[string]struct{}{}\n\tfor _, sourceID := range sourceIDs {\n\t\tif sourceID == targetID {\n\t\t\treturn fmt.Errorf(\"target ID [%s] must not be included in source IDs\", targetID)\n\t\t}\n\t\tif _, ok := seen[sourceID]; ok {\n\t\t\treturn fmt.Errorf(\"duplicate source ID [%s]\", sourceID)\n\t\t}\n\t\tseen[sourceID] = struct{}{}\n\t}\n\treturn nil\n}\n\nfunc isSortableDocument(tree *treenode.BlockTree) bool {\n\treturn nil != tree && tree.ID == tree.RootID && \"d\" == tree.Type && !IsBoxDoc(tree.BoxID, tree.RootID)\n}\n","sourceCodeStart":2704,"sourceCodeEnd":2740,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/file.go#L2704-L2740","documentation":"validateReorderArgs only accepts the literal strings \"before\" or \"after\" as the reorder position. Any other value — including \"\", \"Before\", \"top\", or \"above\" — produces this formatted error naming the offending position. The position determines whether the sources are inserted before or after the target document in the custom sibling order.","triggerScenarios":"Calling ReorderDocs(sourceIDs, targetID, \"above\") or any string other than exactly \"before\"/\"after\"; passing an unvalidated UI dropdown value or an empty position string from plugin/scripted API calls; case mismatch such as \"Before\".","commonSituations":"Hard-coded position constants that don't match the kernel's accepted enum; older client code using different drag-direction vocabulary; dynamically computed positions that end up empty when drag metadata is missing.","solutions":["Pass exactly \"before\" or \"after\" as the position argument","Normalize/validate the position in the caller: p := strings.ToLower(p); if p != \"before\" && p != \"after\" { p = \"before\" }","Check the plugin or script for renamed position constants and align them with the kernel API"],"exampleFix":"// before\nerr := model.ReorderDocs(sourceIDs, targetID, \"above\") // rejected\n// after\nposition := \"before\"\nif dropAfterTarget {\n    position = \"after\"\n}\nerr := model.ReorderDocs(sourceIDs, targetID, position)","handlingStrategy":"validation","validationCode":"if position != \"before\" && position != \"after\" {\n    return fmt.Errorf(\"position must be before or after, got %q\", position)\n}","typeGuard":"func isValidPosition(p string) bool { return p == \"before\" || p == \"after\" }","tryCatchPattern":"if !isValidPosition(position) {\n    position = \"before\" // or surface a validation error\n}","preventionTips":["Use named constants for \"before\"/\"after\" instead of raw strings","Normalize case and trim input before passing position","Keep client-side drag direction vocabulary aligned with the kernel API"],"tags":["validation","enum","argument-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"}