{"record":{"id":"33c66b4e0c0c5035","repo":"siyuan-note/siyuan","slug":"database-block-s-is-already-bound","errorCode":null,"errorMessage":"database block [%s] is already bound","messagePattern":"database block \\[(.+?)\\] is already bound","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/attribute_view_binding_undo.go","lineNumber":141,"sourceCode":"\t}\n\tstate.after = value.Clone()\n\tif inverse != nil {\n\t\top.attributeViewBinding, inverse.attributeViewBinding = state, state\n\t\tinverse.attributeViewBindingUndo = true\n\t}\n\ttargetItemID := value.BlockID\n\tif state.duplicateItemID != \"\" {\n\t\ttargetItemID = state.duplicateItemID\n\t}\n\top.RetData = map[string]any{\"targetItemID\": targetItemID, \"duplicate\": state.duplicateItemID != \"\"}\n\treturn nil\n}\n\nfunc (tx *Transaction) applyAttributeViewBinding(current *av.AttributeView, value, desired *av.Value,\n\tstate *attributeViewBindingSnapshot) error {\n\tif !desired.IsDetached {\n\t\tif existing := current.GetBlockValueByBoundID(desired.Block.ID); existing != nil && existing.BlockID != value.BlockID {\n\t\t\treturn fmt.Errorf(\"database block [%s] is already bound\", desired.Block.ID)\n\t\t}\n\t}\n\tids := []string{}\n\tif !value.IsDetached {\n\t\tids = append(ids, value.Block.ID)\n\t}\n\tif !desired.IsDetached && !slices.Contains(ids, desired.Block.ID) {\n\t\tids = append(ids, desired.Block.ID)\n\t}\n\tnodes, trees := map[string]*ast.Node{}, map[string]*parse.Tree{}\n\tfor _, id := range ids {\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 ErrBlockNotFound\n\t\t}","sourceCodeStart":123,"sourceCodeEnd":159,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/attribute_view_binding_undo.go#L123-L159","documentation":"When applying a new block binding for an attribute view row, the engine first checks that the target block is not already bound to a different row in the same database. A block may be bound to only one row per attribute view; if GetBlockValueByBoundID finds an existing row bound to the desired block ID other than the one being replaced, the operation is rejected.","triggerScenarios":"Calling replaceAttrViewBlock / binding a block to a database where that block's ID already appears as the Block.ID of another row — e.g. duplicating a block that references a bound database entry, or programmatically binding a block that an earlier operation already bound.","commonSituations":"Copy-pasting or duplicating blocks that carry database bindings; importing documents where bound block IDs collide; scripts issuing bind operations without checking existing bindings; sync merging two workspaces that bound the same block independently.","solutions":["Choose a different (unbound) block, or detach the existing row's binding first","Use the duplicate-item path (state.duplicateItemID) so the engine creates a new row instead of rebinding","Query the attribute view for GetBlockValueByBoundID(blockID) before binding to detect the conflict","After sync/merge conflicts, run index rebuild so bindings are consistent, then re-attempt"],"exampleFix":"// before\napi.bindBlockAv({avID, blockID: existingBoundBlockID})\n// after\nconst row = av.keyValues.flatMap(kv => kv.values).find(v => v.block && v.block.id === blockID)\nif (row) api.unbindBlockAv({avID, rowID: row.blockID})\napi.bindBlockAv({avID, blockID})","handlingStrategy":"validation","validationCode":"const existing = av.keyValues.flatMap(kv => kv.values)\n  .find(v => v.block && v.block.id === blockID);\nif (existing && existing.blockID !== targetRowID) {\n  throw new Error('block already bound to row ' + existing.blockID);\n}","typeGuard":"function isBlockFree(av, blockID) {\n  return !av.keyValues.some(kv => kv.values.some(v => v.block && v.block.id === blockID));\n}","tryCatchPattern":null,"preventionTips":["Check existing bindings before binding a block to a database","Use the duplicate-row flow when copying blocks instead of raw rebinding","After importing documents, scan for duplicate bound block IDs and detach extras","Resolve sync conflicts before rebinding so duplicate bindings do not accumulate"],"tags":["database","attribute-view","binding-conflict","duplicate"],"backgroundTag":"entity-already-bound","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"}