siyuan-note/siyuan · error

database entry binding

Error message

database entry binding [%s] changed

What it means

When replaying an insert-database-entries operation, the kernel verifies that each bound (non-detached) row's underlying document node still exists, is bound to this database (validateAttributeViewBinding), and that no other row in the current view is already bound to the same block ID. If the node is gone or another row now holds the binding, replay would corrupt the block-to-row mapping, so it fails with this error.

Solutions

  1. Verify the block still exists in its document and is not already bound to another row in this database
  2. Undo the intermediate operations that moved/deleted the block before replaying
  3. Re-create the missing block and re-add it to the database instead of relying on replay
  4. Re-run indexing/sync so tree and database binding state are consistent, then retry
Defensive patterns

Strategy: validation

Validate before calling

// before replay: confirm each bound block exists and is not already bound elsewhere
for (const id of boundBlockIDs) {
  const node = await getBlockByID(id);
  if (!node) throw new Error(`block ${id} missing`);
  if (currentRows.some(r => r.blockID === id)) throw new Error(`block ${id} already bound`);
}

Try / catch

try { await undo(); } catch (e) { if (/database entry binding \[.+\] changed/.test(e.message)) { reAddEntriesManually(); } else throw e; }

Prevention

When it happens

Trigger: Replaying doInsertAttrViewBlock where a row's bound block was deleted, unbound from the database, or re-bound to another row between the snapshot and the replay — detected via GetNodeInTree returning nil or current.GetBlockValueByBoundID being non-nil.

Common situations: Undo/redo across operations that moved or removed the block; the block was dragged into a different database; a duplicate row binding was created by a plugin or concurrent edit; sync applied changes that rebind blocks.

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@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/17d60aa08460244f. Report an issue: GitHub.

Appendix: source

Thrown at kernel/model/attribute_view_items.go:314

		}
	}
	if err = validateAttributeViewItemKeys(current, state); err != nil {
		return err
	}
	for _, kv := range state.Keys {
		for _, value := range kv.Values {
			if value.Type != av.KeyTypeBlock || value.IsDetached || value.Block == nil {
				continue
			}
			tree, loadErr := tx.loadTree(value.Block.ID)
			if loadErr != nil {
				return loadErr
			}
			if err = validateAttributeViewBinding(state.AvID, tree); err != nil {
				return err
			}
			if treenode.GetNodeInTree(tree, value.Block.ID) == nil || current.GetBlockValueByBoundID(value.Block.ID) != nil {
				return fmt.Errorf("database entry binding [%s] changed", value.Block.ID)
			}
		}
	}
	if err = tx.prepareAttributeViewItemMutation(current, state); err != nil {
		return err
	}
	for _, kv := range state.Keys {
		dest, _ := current.GetKeyValues(kv.Key.ID)
		for _, value := range kv.Values {
			dest.Values = append(dest.Values, value.Clone())
		}
	}
	// 双向关联先在内存中校验并合并,保留目标条目的其他关联。
	changed := map[string]*av.AttributeView{state.AvID: current}
	for _, kv := range state.Keys {
		if kv.Key.Relation == nil || !kv.Key.Relation.IsTwoWay {
			continue
		}

View on GitHub (pinned to 9f775e8a12)