siyuan-note/siyuan · error
duplicate source ID [ ]
Error message
duplicate source ID [%s]
What it means
Each source document may appear only once in a reorder request. validateReorderArgs tracks seen IDs and fails with this error if the same ID occurs twice. Duplicates would corrupt the computed ordering, so they are rejected before any mutation.
Solutions
- Deduplicate sourceIDs (e.g. via a Set) before sending the request
- Fix the selection-merging logic that produces overlapping ranges
- Replace rather than append when retrying a failed reorder with the same IDs
Example fix
// before reorderDocs(rawIDs, targetID, "after") // may contain duplicates // after const sources = [...new Set(rawIDs)].filter(id => id !== targetID) reorderDocs(sources, targetID, "after")
Defensive patterns
Strategy: validation
Validate before calling
const sources = [...new Set(sourceIDs)]
if (sources.length !== sourceIDs.length) { /* deduplicated; proceed with sources */ } Try / catch
try { await api.reorderDocs(src, targetID, pos) } catch (e) { if (String(e).includes("duplicate source ID")) { src = [...new Set(src)]; return retryOnce() } throw e } Prevention
- Deduplicate any list assembled from multiple selection ranges
- Replace (not append) source lists on retry
- Use Set-based collection while accumulating IDs from UI events
When it happens
Trigger: sourceIDs built by concatenating selections that overlap (e.g. two multi-select ranges sharing a document); retry logic appending to an existing list instead of replacing it.
Common situations: Plugins merging drag selections across tree nodes without dedup; idempotency bugs where the same batch is accumulated twice; manually authored API payloads with a repeated ID.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- block [ ] is not a document that can declare a child…
- Conf.Language(112)
- duplicated new item template id
- invalid appearance ID
- invalid document sort mode
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/3f9e4714ea0c2961.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/model/file.go:2754
IncSync()
pushFiletreeSortChanged(sortIDs)
return
}
func validateReorderArgs(sourceIDs []string, targetID, position string) error {
if 1 > len(sourceIDs) {
return errors.New("source IDs must not be empty")
}
if "before" != position && "after" != position {
return fmt.Errorf("invalid reorder position [%s]", position)
}
seen := map[string]struct{}{}
for _, sourceID := range sourceIDs {
if sourceID == targetID {
return fmt.Errorf("target ID [%s] must not be included in source IDs", targetID)
}
if _, ok := seen[sourceID]; ok {
return fmt.Errorf("duplicate source ID [%s]", sourceID)
}
seen[sourceID] = struct{}{}
}
return nil
}
func isSortableDocument(tree *treenode.BlockTree) bool {
return nil != tree && tree.ID == tree.RootID && "d" == tree.Type && !IsBoxDoc(tree.BoxID, tree.RootID)
}
func loadSiblingCustomOrder(boxID, parentPath string, fullSortIDs map[string]int) (ret []string, err error) {
absParentPath := filepath.Join(util.DataDir, boxID, parentPath)
files, err := os.ReadDir(absParentPath)
if nil != err {
return nil, fmt.Errorf("read dir [%s] failed: %w", absParentPath, err)
}
for _, file := range files {
if file.IsDir() || !strings.HasSuffix(file.Name(), ".sy") {View on GitHub (pinned to 9f775e8a12)