siyuan-note/siyuan · warning

attribute view groups changed; retry the drag

Error message

attribute view groups changed; retry the drag

What it means

After the row-order check passes, applyAttributeViewRowOrder compares the number of groups in the submitted RowOrder payload with the live view's group count. A mismatch means the grouping changed since the drag was previewed (group field changed, groups added/removed), so the drag is rejected for retry.

Source

Thrown at kernel/model/attribute_view_row_sort.go:235

	}
	return ret
}

func applyAttributeViewRowOrder(attrView *av.AttributeView, view *av.View, data any) error {
	encoded, err := gulu.JSON.MarshalJSON(data)
	if nil != err {
		return err
	}
	var change attributeViewRowOrderChange
	if err = gulu.JSON.UnmarshalJSON(encoded, &change); nil != err {
		return err
	}
	if nil == change.RowOrder || nil == change.Expected ||
		!reflect.DeepEqual(getAttributeViewRowOrder(view), change.Expected) {
		return errors.New("attribute view order changed; retry the drag")
	}
	if len(change.RowOrder.Groups) != len(view.Groups) {
		return errors.New("attribute view groups changed; retry the drag")
	}
	for _, group := range view.Groups {
		if _, ok := change.RowOrder.Groups[group.ID]; !ok {
			return errors.New("attribute view group not found")
		}
	}
	if nil != change.ValidateGroup && len(change.RowOrder.Sorts) > 0 {
		// 预览之后字段值可能变化,提交时再次校验,不能悄悄接受冲突的落点。
		copyView, copyErr := cloneAttributeViewRowSort(attrView)
		if nil != copyErr {
			return copyErr
		}
		candidate := copyView.GetView(view.ID)
		setAttributeViewRowOrder(candidate, change.RowOrder)
		collections, renderErr := renderAttributeViewRowSortCollections(copyView, candidate, false)
		if nil != renderErr {
			return renderErr
		}

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Reload the view with its current groups, rebuild the drag payload, and retry
  2. Verify the group configuration (group field/settings) is unchanged before committing the drag
  3. If a group change is intended, cancel the pending drag and re-perform it on the new grouping

Example fix

// before
payload.RowOrder.Groups = oldGroups
applyAttributeViewRowOrder(tx, avID, viewID, encode(payload))
// after
payload.RowOrder.Groups = currentGroups(view) // rebuilt from the live view
applyAttributeViewRowOrder(tx, avID, viewID, encode(payload))
Defensive patterns

Strategy: validation

Validate before calling

if (payload.RowOrder.Groups.length !== getViewGroupCount(avID, viewID)) {
  payload = rebuildRowOrderChange(avID, viewID);
}

Try / catch

try {
  await sortAttributeViewRow(avID, viewID, encodedChange);
} catch (e) {
  if (String(e.msg).includes("groups changed")) {
    await reloadAttributeView(avID);
    // cancel or rebuild the drag against the new grouping
  }
}

Prevention

When it happens

Trigger: Calling sortAttributeViewRow when the view's grouping was modified between drag preview and commit — user changed the group-by field, a tab re-grouped the view, or the payload was built from an outdated grouped view.

Common situations: Two windows grouped the same database differently; a plugin changed the group configuration mid-drag; the client replayed an old payload after the view was re-grouped.

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


AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11). Data as JSON: /api/errors/35240382b3663575. Report an issue: GitHub.