{"record":{"id":"3f9e4714ea0c2961","repo":"siyuan-note/siyuan","slug":"duplicate-source-id-s","errorCode":null,"errorMessage":"duplicate source ID [%s]","messagePattern":"duplicate source ID \\[(.+?)\\]","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/file.go","lineNumber":2730,"sourceCode":"\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\nfunc loadSiblingCustomOrder(boxID, parentPath string, fullSortIDs map[string]int) (ret []string, err error) {\n\tabsParentPath := filepath.Join(util.DataDir, boxID, parentPath)\n\tfiles, err := os.ReadDir(absParentPath)\n\tif nil != err {\n\t\treturn nil, fmt.Errorf(\"read dir [%s] failed: %w\", absParentPath, err)\n\t}\n\tfor _, file := range files {\n\t\tif file.IsDir() || !strings.HasSuffix(file.Name(), \".sy\") {","sourceCodeStart":2712,"sourceCodeEnd":2748,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/file.go#L2712-L2748","documentation":"validateReorderArgs rejects repeated IDs within sourceIDs using a seen-set. Each source document should appear exactly once in the move list; duplicates would double-insert the same document in reorderIDSequence and corrupt the resulting order. The error names the first duplicated ID.","triggerScenarios":"Calling ReorderDocs([\"doc-a\", \"doc-a\", \"doc-b\"], targetID, \"before\"); callers that merge selections from multiple origins without deduplicating (e.g. union of dragged + selected lists); loops that append an ID per event instead of once per document.","commonSituations":"Multi-select drag implementations that fire once per dragged item and concatenate IDs; bulk automation scripts processing duplicates from a stale index; event handlers accumulating selection state across drags.","solutions":["Deduplicate sourceIDs before the call, e.g. via a map or slices.Contains filter","Fix the caller so each document is added to the sources list only once per operation","If merging multiple selection sources, dedupe after the merge"],"exampleFix":"// before\nerr := model.ReorderDocs(sourceIDs, targetID, position) // may contain dupes\n// after\nseen := map[string]bool{}\nunique := sourceIDs[:0]\nfor _, id := range sourceIDs {\n    if !seen[id] {\n        seen[id] = true\n        unique = append(unique, id)\n    }\n}\nerr := model.ReorderDocs(unique, targetID, position)","handlingStrategy":"validation","validationCode":"seen := map[string]bool{}\nfor _, id := range sourceIDs {\n    if seen[id] {\n        return errors.New(\"duplicate source ID: \" + id)\n    }\n    seen[id] = true\n}","typeGuard":"func hasNoDuplicates(ids []string) bool {\n    seen := make(map[string]struct{}, len(ids))\n    for _, id := range ids {\n        if _, ok := seen[id]; ok { return false }\n        seen[id] = struct{}{}\n    }\n    return true\n}","tryCatchPattern":"if !hasNoDuplicates(sourceIDs) {\n    sourceIDs = dedupe(sourceIDs) // dedupe before calling\n}","preventionTips":["Deduplicate after merging selections from multiple origins","Append each document ID once per operation, not once per drag event","Cover duplicate handling with unit tests in selection-merge code"],"tags":["validation","argument-validation","go"],"backgroundTag":"invalid-argument-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"}