{"record":{"id":"3127e258a41bd7ed","repo":"siyuan-note/siyuan","slug":"database-binding-s-already-exists","errorCode":null,"errorMessage":"database binding [%s] already exists","messagePattern":"database binding \\[(.+?)\\] already exists","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/attribute_view_block_undo.go","lineNumber":120,"sourceCode":"func (tx *Transaction) restoreDeletedAttributeViewBlocks(op *Operation) error {\n\tstate := op.attributeViewFields\n\tif err := tx.rememberAttributeViewMutationTree(state.blockID); err != nil {\n\t\treturn err\n\t}\n\tfor _, src := range op.Srcs {\n\t\tid, _ := src[\"id\"].(string)\n\t\tif err := tx.rememberAttributeViewMutationTree(id); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tcurrent, err := tx.readAttributeViewForMutation(op.AvID, state.blockID, state.boxID)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor _, src := range op.Srcs {\n\t\tid, _ := src[\"id\"].(string)\n\t\tif current.GetBlockValueByBoundID(id) != nil {\n\t\t\treturn fmt.Errorf(\"database binding [%s] already exists\", id)\n\t\t}\n\t\tnode, tree, err := getNodeByBlockID(tx, id)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif node == nil {\n\t\t\treturn fmt.Errorf(\"restored database binding [%s] is missing\", id)\n\t\t}\n\t\tif err = validateAttributeViewBinding(op.AvID, tree); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\tif err := tx.replayAttributeViewFields(op); err != nil {\n\t\treturn err\n\t}\n\trestored, err := tx.readAttributeViewForMutation(op.AvID, state.blockID, state.boxID)\n\tif err != nil {\n\t\treturn err","sourceCodeStart":102,"sourceCodeEnd":138,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/attribute_view_block_undo.go#L102-L138","documentation":"restoreDeletedAttributeViewBlocks is the undo operation (inverse of insertAttrViewBlock) that re-inserts database rows deleted in the same transaction and re-binds them to their document blocks. Before replaying, it verifies that no live row in the database is already bound to each block ID (GetBlockValueByBoundID(id) == nil). If a row is already bound to that block, re-inserting would create a duplicate binding, so the undo fails with the block ID.","triggerScenarios":"Undoing a transaction that deleted attribute-view rows, where a row bound to the same document block was meanwhile re-created in the same database (e.g. the user re-added the block to the database, or a prior undo/redo partially restored bindings), so current.GetBlockValueByBoundID(id) returns non-nil.","commonSituations":"Redo/undo cycles interleaved with manual re-adds of the block to the database; repeated undo invocations after a partially applied undo; concurrent clients (sync or multiple windows) re-creating rows for the same block.","solutions":["Delete the row currently bound to that block ID in the database so the undo can restore the original row, then retry the undo","Undo again from an earlier point in the history panel instead of transactional undo","Verify the existing binding points to the intended row; if it is already correct, no undo of the deletion is needed for that row — remove its entry from the undo operation","Check for duplicate bindings via the database UI and clean up stale rows before retrying"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Go: before undoing a row deletion, check the binding is free\nif current.GetBlockValueByBoundID(blockID) != nil {\n    // a row is already bound; resolve before undo\n}","typeGuard":"if current.GetBlockValueByBoundID(id) == nil {\n    // safe to re-insert and bind\n}","tryCatchPattern":"err := tx.restoreDeletedAttributeViewBlocks(op)\nif err != nil && strings.Contains(err.Error(), \"already exists\") {\n    // remove the conflicting row or skip this Src entry, then retry\n}","preventionTips":["Undo deletions in order (blocks first, rows second)","Avoid re-adding blocks to the database between delete and undo","Prevent concurrent undo from multiple windows/sync","Verify bindings before manual row re-creation"],"tags":["database","undo","attribute-view","binding-conflict"],"backgroundTag":"invalid-state-transition","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}