siyuan-note/siyuan · error

database entry [ ] was removed after the field

Error message

database entry [%s] was removed after the field

What it means

After applying the recorded patch, replayAttributeViewFields checks that any field values which are re-added during replay still have their owning rows: for every restored KeyValues entry whose field is not yet present in the live database, each value's BlockID must still exist as a block value (restored.GetBlockValue). If a row (database entry) that existed when the field was removed has since been deleted, replaying would resurrect orphaned field values, so the kernel refuses with this error.

Solutions

  1. Re-create (or restore via document history) the deleted rows before undoing the field removal
  2. Skip this undo step; re-add the field and re-enter the surviving values manually
  3. Adjust the snapshot/undo ordering so row deletions are undone before the field deletion

Example fix

// before
tx.undo(removeColumnOp) // rows already deleted -> error
// after
for _, rowID := range removedRowIDs {
    if currentAv.GetBlockValue(rowID) == nil {
        // restore row first (history/insertAttrViewBlock) before undoing the column
        restoreRow(rowID)
    }
}
Defensive patterns

Strategy: validation

Validate before calling

for _, kv := range snapshotRestored.KeyValues {
    if _, err := current.GetKey(kv.Key.ID); err == nil { continue }
    for _, v := range kv.Values {
        if current.GetBlockValue(v.BlockID) == nil {
            return fmt.Errorf("row %s missing; restore rows before undoing field deletion", v.BlockID)
        }
    }
}

Type guard

func rowsExist(av *av.AttributeView, ids []string) bool {
    for _, id := range ids { if av.GetBlockValue(id) == nil { return false } }
    return true
}

Try / catch

if err := tx.replayAttributeViewFields(op); err != nil {
    if strings.Contains(err.Error(), "was removed after the field") {
        // restore rows from history first, then retry undo
        return nil
    }
    return err
}

Prevention

When it happens

Trigger: Undoing a column deletion whose snapshot contains field values for rows that were themselves removed later (row deleted by user, plugin, or sync between the field deletion and the undo replay).

Common situations: User deleted rows right after removing a column and then hit undo repeatedly; sync merged a row deletion from another device; a plugin pruned detached rows while the undo stack still referenced them.

Related errors


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

Appendix: source

Thrown at kernel/model/attribute_view_fields.go:287

		patched, _, err := state.changes[id].apply(data, true, op.attributeViewFieldUndo)
		if err != nil {
			return fmt.Errorf("database [%s]: %w", id, err)
		}
		encoded, err := json.Marshal(patched)
		if err != nil {
			return err
		}
		restored := &av.AttributeView{}
		if err = json.Unmarshal(encoded, restored); err != nil {
			return err
		}
		for _, kv := range restored.KeyValues {
			if _, keyErr := current.GetKey(kv.Key.ID); keyErr == nil {
				continue
			}
			for _, value := range kv.Values {
				if restored.GetBlockValue(value.BlockID) == nil {
					return fmt.Errorf("database entry [%s] was removed after the field", value.BlockID)
				}
			}
		}
		before[id], after[id] = current, restored
	}
	return tx.saveAttributeViewFieldChanges(state, before, after)
}

func (tx *Transaction) saveAttributeViewFieldChanges(state *attributeViewFieldsSnapshot, before, after map[string]*av.AttributeView) error {
	if err := tx.rememberAttributeViewMutationTree(state.blockID); err != nil {
		return err
	}
	ids := sortedAttributeViewFieldKeys(after)
	for _, id := range ids {
		key := state.boxID + "/" + id
		if tx.attributeViewRollback.views[key] == nil {
			tx.attributeViewRollback.views[key] = before[id]
		}

View on GitHub (pinned to 9f775e8a12)