siyuan-note/siyuan · warning
attribute view drop target changed; retry the drag
Error message
attribute view drop target changed; retry the drag
What it means
After splitting rows into moved/remaining, the function locates the drop position using previousID/nextID within `remaining`. If neither anchor is found (index stays -1), the drop target row no longer exists in the un-selected rows — the view changed during the drag — so the operation is rejected for a retry.
Source
Thrown at kernel/model/attribute_view_row_sort.go:198
}
}
if len(moved) != len(selectedIDs) {
return nil, errors.New("attribute view rows changed; retry the drag")
}
if selectedIDs[nextID] || ("" == nextID && selectedIDs[previousID]) {
return slices.Clone(ordered), nil
}
index := 0
if "" != nextID {
index = slices.Index(remaining, nextID)
} else if "" != previousID {
index = slices.Index(remaining, previousID)
if index >= 0 {
index++
}
}
if index < 0 {
return nil, errors.New("attribute view drop target changed; retry the drag")
}
ret := append([]string{}, remaining[:index]...)
ret = append(ret, moved...)
return append(ret, remaining[index:]...), nil
}
// mergeAttributeViewRowOrder 保留当前尚未参与渲染的项目 ID,避免用页面数据覆盖完整列表。
func mergeAttributeViewRowOrder(original, ordered []string) []string {
ret := append([]string{}, ordered...)
seen := map[string]bool{}
for _, id := range ordered {
seen[id] = true
}
for _, id := range original {
if !seen[id] {
ret = append(ret, id)
seen[id] = true
}View on GitHub (pinned to 8641553a1f)
Solutions
- Refresh the view's row order and current drop anchors from the frontend, then retry the drag
- Verify previousID/nextID still exist in the view and are not part of the selected set before submitting
- Discard the stale drag request and re-initiate it from the current UI state
Example fix
// before
rows, err := moveAttributeViewSortedRows(ordered, selected, prevID, nextID)
// after
if slices.Contains(ordered, prevID) || slices.Contains(ordered, nextID) || nextID == "" {
rows, err = moveAttributeViewSortedRows(ordered, selected, prevID, nextID)
} else {
// refresh anchors then retry
} Defensive patterns
Strategy: validation
Validate before calling
const rows = getVisibleRowIds(avID, viewID); if (prevID && !rows.includes(prevID)) return; if (nextID && !rows.includes(nextID)) return;
Try / catch
try {
await sortAttributeViewRows(avID, viewID, selected, prevID, nextID);
} catch (e) {
if (String(e.msg).includes("drop target changed")) {
// refresh anchors and let the user re-drag
}
} Prevention
- Validate drop anchors exist in the current view before submitting
- Cancel pending drags when rows are deleted by sync or other windows
- Re-derive previousID/nextID from live DOM state at commit time
When it happens
Trigger: Calling prepareAttributeViewRowSort where previousID/nextID (the drop anchors submitted by the frontend) are missing from the remaining rows — anchor row was deleted, became itself selected, or the drag payload references an outdated view.
Common situations: Concurrent edits removing the anchor row while a user drags; stale frontend state after another tab moved the same rows; replayed/queued drag requests referencing old row IDs after sync.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- attribute view rows changed; retry the drag
- attribute view order changed; retry the drag
- attribute view sort values changed; retry the drag
- no attribute view rows selected
- attribute view groups changed; retry the drag
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/0258198c8d21b1eb.
Report an issue: GitHub.