{"record":{"id":"b1c4e22b73efc90b","repo":"siyuan-note/siyuan","slug":"database-entry-s-has-a-different-binding","errorCode":null,"errorMessage":"database entry [%s] has a different binding","messagePattern":"database entry \\[(.+?)\\] has a different binding","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/attribute_view_block_history.go","lineNumber":170,"sourceCode":"\t\t\tcontinue\n\t\t}\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tcurrent, err := load(id)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tvar itemIDs []string\n\t\tfor _, primary := range historical.GetBlockKeyValues().Values {\n\t\t\tif primary.IsDetached || primary.Block == nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif _, exists := bound[id][primary.Block.ID]; !exists || current.GetBlockValueByBoundID(primary.Block.ID) != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif current.GetBlockValue(primary.BlockID) != nil {\n\t\t\t\treturn fmt.Errorf(\"database entry [%s] has a different binding\", primary.BlockID)\n\t\t\t}\n\t\t\titemIDs = append(itemIDs, primary.BlockID)\n\t\t}\n\t\tif len(itemIDs) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tfor _, kv := range historical.KeyValues {\n\t\t\tfor _, value := range kv.Values {\n\t\t\t\tif !slices.Contains(itemIDs, value.BlockID) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tfield, err := current.GetKeyValues(kv.Key.ID)\n\t\t\t\tif err != nil || field.Key.Type != kv.Key.Type || !reflect.DeepEqual(field.Key.Relation, kv.Key.Relation) {\n\t\t\t\t\treturn fmt.Errorf(\"database field [%s] changed since document deletion\", kv.Key.ID)\n\t\t\t\t}\n\t\t\t\tif field.GetValue(value.BlockID) == nil {\n\t\t\t\t\tfield.Values = append(field.Values, value.Clone())\n\t\t\t\t}","sourceCodeStart":152,"sourceCodeEnd":188,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/attribute_view_block_history.go#L152-L188","documentation":"During document rollback, restoreBoundAttributeViewHistory merges missing rows from the historical database snapshot back into the current database. For each historical bound (non-detached) row whose block ID is missing from the current data, it requires that no other row in the current database is already bound to that block (GetBlockValue(primary.BlockID) == nil). If a different row already occupies that block binding, the historical row cannot be re-attached and the whole restore is aborted with this error.","triggerScenarios":"RollbackDocHistory on a document that contained bound attribute-view rows, where since the deletion someone created a new row in the same database bound to the same document block ID (e.g. via addAttrViewBlock with a blockID, or a re-add after partial restore), while the historical snapshot still lists the original row for that block.","commonSituations":"User re-added the block to the database manually after deleting the document, then rolled back the document; concurrent edits between deletion and rollback; partial restores that left stale bindings; undo/redo interleaving with history rollback.","solutions":["Remove the conflicting row currently bound to that block ID from the database (or unbind it) so the historical row can be restored, then retry the rollback","Choose an earlier history version where no conflicting binding existed","If the current row is the desired one, exclude the database merge by restoring only the document tree (ensure the snapshot lacks the conflicting rows or skip attribute-view restore)","Manually recreate the deleted rows instead of rollback if bindings are heavily diverged"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Go: before rollback, detect block-binding conflicts\nfor _, primary := range historical.GetBlockKeyValues().Values {\n    if primary.Block == nil || primary.IsDetached { continue }\n    if current.GetBlockValue(primary.BlockID) != nil {\n        // conflicting binding exists; resolve before rollback\n    }\n}","typeGuard":"if current.GetBlockValueByBoundID(blockID) == nil && current.GetBlockValue(blockID) == nil {\n    // safe to restore this row\n}","tryCatchPattern":"err := tx.restoreBoundAttributeViewHistory(tree, historyDir)\nif err != nil {\n    var conflictErr *fmt.Errorf\n    if strings.Contains(err.Error(), \"has a different binding\") {\n        // unbind/delete the conflicting row, then retry\n    }\n    return err\n}","preventionTips":["Do not re-add deleted blocks to the database before rolling back","Check existing bindings before manually recreating rows","Avoid concurrent edits during history rollback","Prefer full history rollback over partial row re-creation"],"tags":["database","history","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"}