siyuan-note/siyuan · error
database entry binding
Error message
database entry binding [%s] changed
What it means
During a database binding replacement (bind/replace block in an attribute view) the transaction verifies the current on-disk row value still matches the snapshot recorded when the operation was queued. If the value is missing or its ID, key ID, detached flag, or bound block ID changed, the transaction aborts to avoid overwriting a concurrent modification.
Solutions
- Retry the operation after the conflicting change settles (close other editors/sync, then re-bind)
- Reload the attribute view so a fresh snapshot is captured and redo the replace
- Recreate the affected database row if its value record is permanently out of sync
- If this appears during undo/redo only, report the transaction-log sequence as a bug
Defensive patterns
Strategy: retry
Validate before calling
const current = av.rows.find(r => r.id === expected.blockID);
if (!current || current.valueID !== expected.id || current.keyID !== expected.keyID) {
refreshAttributeView(avID);
} Try / catch
try {
await api.replaceAttrViewBlock(op);
} catch (e) {
if (/database entry binding .* changed/.test(e.msg)) {
await reloadAV(avID);
await api.replaceAttrViewBlock(op);
}
} Prevention
- Avoid concurrent edits to the same database row from multiple clients
- Complete or cancel pending sync before rebinding blocks
- Reload the attribute view after history restores before issuing more binding ops
- Serialize programmatic binding operations through one queue
When it happens
Trigger: replaceAttributeViewBinding executed after another transaction already changed the row: the block was rebound elsewhere, the row was detached/re-keyed, or the entry was deleted so GetBlockValue(expected.BlockID) returns nil or a value with a different Value.ID/KeyID/Block.ID than the captured expectation.
Common situations: Two windows or a sync job editing the same database concurrently; undo/redo racing with live edits; replaying an operation against an attribute view that was re-indexed or restored from history between queueing and execution.
Related errors
- attribute view not found
- attribute view order changed; retry the drag
- attribute view rows changed; retry the drag
- attribute view [ ] changed while loading search info
- cannot remove primary key field
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/7fff2064e4dda7c3.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/model/attribute_view_binding_undo.go:58
if !tx.isReplay || state.avID != op.AvID || state.before.BlockID != op.PreviousID {
return fmt.Errorf("invalid database binding replay")
}
current, err := tx.readAttributeViewForMutation(state.avID, state.blockID, state.boxID)
if err != nil {
return err
}
if state.duplicateItemID != "" {
op.RetData = map[string]any{"targetItemID": state.duplicateItemID, "duplicate": true}
return nil
}
expected, desired := state.before, state.after
if op.attributeViewBindingUndo {
expected, desired = desired, expected
}
value := current.GetBlockValue(expected.BlockID)
if value == nil || value.Block == nil || value.ID != expected.ID || value.KeyID != expected.KeyID ||
value.IsDetached != expected.IsDetached || value.Block.ID != expected.Block.ID {
return fmt.Errorf("database entry binding [%s] changed", expected.BlockID)
}
if err = tx.applyAttributeViewBinding(current, value, desired, state); err != nil {
return err
}
op.RetData = map[string]any{"targetItemID": value.BlockID, "duplicate": false}
return nil
}
var inverse *Operation
if tx.fromAPI && !tx.isReplay {
for _, undo := range tx.UndoOperations {
if undo.Action == "replaceAttrViewBlock" && undo.AvID == op.AvID &&
undo.PreviousID == op.PreviousID && undo.attributeViewBinding == nil {
inverse = undo
break
}
}
}View on GitHub (pinned to 9f775e8a12)