siyuan-note/siyuan · warning
attribute view rows changed; retry the drag
Error message
attribute view rows changed; retry the drag
What it means
moveAttributeViewSortedRows filters the current row order into `moved` (selected rows) and `remaining`. If the number of matched selected rows differs from the number of distinct selected IDs, some selected rows no longer exist in the ordered list — the attribute view changed under the drag. The operation is rejected so the client retries with fresh data.
Source
Thrown at kernel/model/attribute_view_row_sort.go:183
func moveAttributeViewSortedRows(ordered, selected []string, previousID, nextID string) ([]string, error) {
selectedIDs := map[string]bool{}
for _, id := range selected {
selectedIDs[id] = true
}
if 0 == len(selectedIDs) {
return nil, errors.New("no attribute view rows selected")
}
var moved, remaining []string
for _, id := range ordered {
if selectedIDs[id] {
moved = append(moved, id)
} else {
remaining = append(remaining, id)
}
}
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...)View on GitHub (pinned to 8641553a1f)
Solutions
- Reload the attribute view row order and re-capture the selection, then retry the drag (the error message explicitly asks for a retry)
- Reconcile the selection against current rows: drop selected IDs that no longer exist and re-submit
- Check for concurrent editors/sync activity and re-attempt after the view stabilizes
Example fix
// before
rows, err := moveAttributeViewSortedRows(ordered, selected, prevID, nextID)
if err != nil { return err }
// after
rows, err := moveAttributeViewSortedRows(ordered, selected, prevID, nextID)
if err != nil {
ordered, selected = refreshRowOrderAndSelection(avID) // re-read current state
rows, err = moveAttributeViewSortedRows(ordered, selected, prevID, nextID)
} Defensive patterns
Strategy: retry
Validate before calling
const currentIds = getVisibleRowIds(avID, viewID);
const stillValid = selected.every(id => currentIds.includes(id));
if (!stillValid) { refreshSelection(); } Try / catch
try {
await sortAttributeViewRows(avID, viewID, selected, prevID, nextID);
} catch (e) {
if (String(e.msg).includes("attribute view rows changed")) {
await reloadAttributeView(avID);
// retry once with fresh selection
}
} Prevention
- Re-capture the selection right before committing the drag
- Handle sync/deletion events by cancelling in-flight drags
- Retry once after reloading the view, then surface the conflict to the user
When it happens
Trigger: Calling prepareAttributeViewRowSort when one or more selected row IDs are absent from the view's current row order — rows deleted by another client/tab, block IDs changed after a transaction, or the drag started against a stale view snapshot.
Common situations: Two editors open on the same database, one deletes a row while the other drags; sync applied remote row deletions between drag start and commit; a plugin removed rows concurrently.
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 drop target 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/b6b3d4bdeb9774ad.
Report an issue: GitHub.