{"record":{"id":"eaeccba31db7c8e6","repo":"siyuan-note/siyuan","slug":"database-entry-s-has-newer-field-values","errorCode":null,"errorMessage":"database entry [%s] has newer field values","messagePattern":"database entry \\[(.+?)\\] has newer field values","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/attribute_view_items.go","lineNumber":62,"sourceCode":"\t\tcurrent, err := tx.readAttributeViewItems(op.attributeViewItems)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err = validateAttributeViewItemKeys(current, op.attributeViewItems); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tfor _, kv := range op.attributeViewItems.Keys {\n\t\t\tfor _, value := range kv.Values {\n\t\t\t\tif !reflect.DeepEqual(value, av.GetValue(current.KeyValues, kv.Key.ID, value.BlockID)) {\n\t\t\t\t\treturn fmt.Errorf(\"database entry [%s] changed since deletion was undone\", value.BlockID)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfor _, kv := range current.KeyValues {\n\t\t\tfor _, value := range kv.Values {\n\t\t\t\tif slices.Contains(op.attributeViewItems.ItemIDs, value.BlockID) &&\n\t\t\t\t\t!reflect.DeepEqual(value, av.GetValue(op.attributeViewItems.Keys, kv.Key.ID, value.BlockID)) {\n\t\t\t\t\treturn fmt.Errorf(\"database entry [%s] has newer field values\", value.BlockID)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif err = tx.prepareAttributeViewItemMutation(current, op.attributeViewItems); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn tx.replayAttributeViewItemConfiguration(op.attributeViewItems, false)\n\t}\n\tif tx.isReplay || !tx.fromAPI || len(tx.UndoOperations) == 0 {\n\t\treturn nil\n\t}\n\tcurrent, err := avParseView(op.AvID, op.BlockID)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif current == nil {\n\t\treturn av.ErrViewNotFound\n\t}","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/attribute_view_items.go#L44-L80","documentation":"In the same replay check, prepareAttributeViewItemRemoval walks the live database's KeyValues and, for every row scheduled for removal (op.attributeViewItems.ItemIDs), requires that the current value deep-equals the value recorded in the snapshot. If the live row carries values newer than the snapshot (row was edited after the deletion snapshot captured the 'older' state), it fails with 'database entry [%s] has newer field values' rather than deleting fresher data.","triggerScenarios":"Undo replay of doRemoveAttrViewBlock where a row present in ItemIDs was modified after the deletion snapshot was taken, so the current cell differs from op.attributeViewItems.Keys (checked only for rows in ItemIDs, complementing errorIndex 957 which checks snapshot values against current).","commonSituations":"User deleted a row via the database UI while a background process/plugin kept updating that row's cells; sync applied remote edits to the row after local deletion; undo stack replayed partially across sessions.","solutions":["Resolve the newer edits first (keep or merge them), then re-issue the removal without the stale snapshot","Undo the row edits before undoing the deletion so the snapshot matches again","Refresh the snapshot by performing the deletion anew on the current data"],"exampleFix":"// before\ntx.removeAttrViewBlock(avID, itemIDs) // stale snapshot deletes newer edits\n// after\nfor _, kv := range current.KeyValues {\n    for _, v := range kv.Values {\n        if slices.Contains(itemIDs, v.BlockID) && !reflect.DeepEqual(v, snapValue) {\n            // abort and re-capture snapshot from current data\n            return nil\n        }\n    }\n}","handlingStrategy":"validation","validationCode":"for _, kv := range current.KeyValues {\n    for _, v := range kv.Values {\n        if slices.Contains(itemIDs, v.BlockID) && !reflect.DeepEqual(v, av.GetValue(snap.Keys, kv.Key.ID, v.BlockID)) {\n            return fmt.Errorf(\"row %s has newer values; re-capture snapshot\", v.BlockID)\n        }\n    }\n}","typeGuard":"func rowsUnchangedSince(current *av.AttributeView, snap *model.AttributeViewItemsSnapshot, ids []string) bool {\n    for _, kv := range current.KeyValues {\n        for _, v := range kv.Values {\n            if slices.Contains(ids, v.BlockID) && !reflect.DeepEqual(v, av.GetValue(snap.Keys, kv.Key.ID, v.BlockID)) { return false }\n        }\n    }\n    return true\n}","tryCatchPattern":"if err := tx.prepareAttributeViewItemRemoval(op); err != nil {\n    if strings.Contains(err.Error(), \"has newer field values\") {\n        // keep newer data: re-issue deletion from current state instead of stale undo\n        return nil\n    }\n    return err\n}","preventionTips":["Stop background writers (plugins, automation) before deleting rows","Perform deletions from the current state rather than replaying old undos","Reconcile sync conflicts on the rows before undoing deletions"],"tags":["database","attribute-view","undo-redo","conflict","data-loss"],"backgroundTag":"conflicting-concurrent-edit","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"}