siyuan-note/siyuan · error

database entry [ ] already exists

Error message

database entry [%s] already exists

What it means

During transaction replay (undo/redo of an 'insert database entries' operation), the kernel re-applies a snapshot of previously inserted rows. Before restoring, it scans the current attribute view for any value whose BlockID is already in the snapshot's item list; if one exists, restoring would duplicate the row, so it aborts with this error. It is an internal consistency guard ensuring idempotent replay of entry insertion.

Solutions

  1. Check the current database for rows with those block IDs and remove the duplicates before retrying the transaction
  2. Clear the conflicting transaction history (undo the intermediate operations) so replay no longer collides
  3. Avoid inserting rows bound to existing block IDs that already appear in the same database; let the kernel generate fresh item IDs
  4. If triggered by sync, resolve the sync conflict so both sides agree on which rows exist

Example fix

// before: re-inserting rows whose block IDs still exist
attrsView.InsertRows(existingBlockIDs...)
// after: only insert IDs not already present in the database
ids := filter(existingBlockIDs, func(id string) bool { return current.GetBlockValue(id) == nil })
attrsView.InsertRows(ids...)
Defensive patterns

Strategy: try-catch

Validate before calling

// client-side: ensure target IDs are not already rows in the database
const exists = await getAttrView(avID);
const dup = exists.keyValues.flatMap(kv => kv.values.map(v => v.blockID)).filter(id => itemIDs.includes(id));
if (dup.length) throw new Error(`rows already present: ${dup.join(',')}`);

Try / catch

try { await insertAttrViewBlock(avID, blockID, items); } catch (e) { if (/database entry \[.+\] already exists/.test(e.message)) { await removeDuplicateRows(avID, itemIDs); retry(); } else throw e; }

Prevention

When it happens

Trigger: Replaying a doInsertAttrViewBlock operation whose target rows were re-created (same BlockIDs already present in the attribute view) between the original deletion and the replay — e.g. an undo/redo sequence that re-inserts entries that were later re-added with the same block IDs.

Common situations: Undo/redo history interleaved with other edits; rows restored by sync or another transaction before the replay runs; a plugin or script inserting rows with explicit block IDs that collide with the replayed snapshot.

Understand the failure class

Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/22955dc0b8318d49. Report an issue: GitHub.

Appendix: source

Thrown at kernel/model/attribute_view_items.go:294

			return err
		}
	}
	return nil
}

func (tx *Transaction) restoreAttributeViewItems(op *Operation) error {
	state := op.attributeViewItems
	if !tx.isReplay || op.AvID != state.AvID {
		return fmt.Errorf("invalid database entry replay")
	}
	current, err := tx.readAttributeViewItems(state)
	if err != nil {
		return err
	}
	for _, kv := range current.KeyValues {
		for _, value := range kv.Values {
			if slices.Contains(state.ItemIDs, value.BlockID) {
				return fmt.Errorf("database entry [%s] already exists", value.BlockID)
			}
		}
	}
	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
			}

View on GitHub (pinned to 9f775e8a12)