siyuan-note/siyuan · error
database field [ ] changed since document deletion
Error message
database field [%s] changed since document deletion
What it means
When merging historical rows back, restoreBoundAttributeViewHistory checks each field (key) in the snapshot against the corresponding live field: the key must exist, have the same type, and an identical Relation definition (deep-equal). If the live database's field schema changed since the document was deleted (type changed, relation re-pointed, or key removed), historical values cannot be safely re-attached and the rollback aborts.
Solutions
- Restore the database field to its pre-deletion definition (same key ID, type, and relation settings) and retry the rollback
- Pick an earlier document-history version taken before the schema change
- If the schema change is intended, manually copy the historical values into the new field instead of relying on rollback
- Export the historical row data from the snapshot JSON for reference, then rebuild rows manually
Defensive patterns
Strategy: try-catch
Validate before calling
// Go: before rollback, verify field schema matches snapshot
field, err := current.GetKeyValues(kv.Key.ID)
schemaOK := err == nil && field.Key.Type == kv.Key.Type &&
reflect.DeepEqual(field.Key.Relation, kv.Key.Relation) Try / catch
err := tx.restoreBoundAttributeViewHistory(tree, historyDir)
if err != nil && strings.Contains(err.Error(), "changed since document deletion") {
// restore the field definition or pick an earlier history version
} Prevention
- Freeze database schema changes until pending rollbacks are done
- Document field ID/type/relation before deleting documents for later restore
- Avoid plugins/API scripts that mutate key definitions
- Roll back before restructuring the database
When it happens
Trigger: RollbackDocHistory on a document with attribute-view rows, where between deletion and rollback a field in the bound database was deleted, changed type (e.g. text→number), or its relation settings (AvID/BackKeyID/IsTwoWay) were modified, so GetKeyValues(kv.Key.ID) fails or the type/relation comparison fails.
Common situations: User edited the database schema after deleting a document and then rolled back; migration or plugin scripts altering field definitions; restoring across workspace versions where serialization of Relation differs; merging snapshots from databases that were since restructured.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- database back relation changed since document deletion
- database back relation value is invalid
- database entry [ ] has a different binding
- database field [ ] changed since entries were deleted
- database history contains an invalid group
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/27b788b8ae64f731.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/model/attribute_view_block_history.go:184
if _, exists := bound[id][primary.Block.ID]; !exists || current.GetBlockValueByBoundID(primary.Block.ID) != nil {
continue
}
if current.GetBlockValue(primary.BlockID) != nil {
return fmt.Errorf("database entry [%s] has a different binding", primary.BlockID)
}
itemIDs = append(itemIDs, primary.BlockID)
}
if len(itemIDs) == 0 {
continue
}
for _, kv := range historical.KeyValues {
for _, value := range kv.Values {
if !slices.Contains(itemIDs, value.BlockID) {
continue
}
field, err := current.GetKeyValues(kv.Key.ID)
if err != nil || field.Key.Type != kv.Key.Type || !reflect.DeepEqual(field.Key.Relation, kv.Key.Relation) {
return fmt.Errorf("database field [%s] changed since document deletion", kv.Key.ID)
}
if field.GetValue(value.BlockID) == nil {
field.Values = append(field.Values, value.Clone())
}
}
}
for _, view := range current.Views {
if old := historical.GetView(view.ID); old != nil {
view.ItemIDs = restoreAttributeViewItemOrder(view.ItemIDs, old.ItemIDs, itemIDs)
for _, group := range view.Groups {
if oldGroup := old.GetGroupByID(group.ID); oldGroup != nil {
group.GroupItemIDs = restoreAttributeViewItemOrder(group.GroupItemIDs, oldGroup.GroupItemIDs, itemIDs)
}
}
}
}
for _, itemID := range itemIDs {
if covers := historical.CardCoverPositions[itemID]; covers != nil {View on GitHub (pinned to 9f775e8a12)