{"record":{"id":"ada3ebe489d62525","repo":"siyuan-note/siyuan","slug":"database-field-undo-conflicts-with-a-subsequent-edit","errorCode":null,"errorMessage":"database field undo conflicts with a subsequent edit","messagePattern":"database field undo conflicts with a subsequent edit","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"kernel/model/attribute_view_field_changes.go","lineNumber":207,"sourceCode":"\t}\n\tchange.elements = map[string]*attributeViewFieldChange{}\n\tchange.beforeOrder, change.afterOrder = oldOrder, newOrder\n\tfor _, id := range append(append([]string(nil), oldOrder...), newOrder...) {\n\t\told, oldExists := oldElements[id]\n\t\tnewValue, newExists := newElements[id]\n\t\tif child := diffAttributeViewFieldData(old, newValue, oldExists, newExists, container); child != nil {\n\t\t\tchange.elements[id] = child\n\t\t}\n\t}\n\tif len(change.elements) == 0 {\n\t\treturn nil\n\t}\n\tchange.before, change.after = nil, nil\n\treturn change\n}\n\nfunc (change *attributeViewFieldChange) apply(current any, exists, undo bool) (any, bool, error) {\n\tconflict := fmt.Errorf(\"database field undo conflicts with a subsequent edit\")\n\tif change.members != nil {\n\t\tobject, ok := current.(map[string]any)\n\t\tif !exists || !ok {\n\t\t\treturn nil, false, conflict\n\t\t}\n\t\tfor _, key := range sortedAttributeViewFieldKeys(change.members) {\n\t\t\tvalue, present := object[key]\n\t\t\treplacement, keep, err := change.members[key].apply(value, present, undo)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, false, fmt.Errorf(\"%s: %w\", key, err)\n\t\t\t}\n\t\t\tif keep {\n\t\t\t\tobject[key] = replacement\n\t\t\t} else {\n\t\t\t\tdelete(object, key)\n\t\t\t}\n\t\t}\n\t\treturn object, true, nil","sourceCodeStart":189,"sourceCodeEnd":225,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/attribute_view_field_changes.go#L189-L225","documentation":"apply is the undo engine for recorded attribute-view field changes. When undoing a change, it expects the current JSON structure to match the shape recorded at edit time (change.members != nil requires current to be an existing map). If the current value is missing (exists==false) or has a different type, the recorded change cannot be reversed, so the undo is rejected with this conflict error. It protects against silently corrupting data when later edits diverged from the undo log.","triggerScenarios":"Undoing a database field edit after subsequent edits restructured the underlying map: the target key/value was deleted, the container itself was removed, or the stored value is not a map[string]any anymore.","commonSituations":"Undoing many steps where an intermediate operation deleted the database field; redo/undo after a sync merged conflicting changes; corrupted or manually edited JSON in the attribute view storage.","solutions":["Stop undoing past the conflict point — the undo stack is no longer linearly reversible; refresh the document instead","Redo/redo-history: restore the desired state via history (data history feature) rather than undo","Avoid manual edits to attribute view storage so recorded shapes stay consistent with live data","If it happens reproducibly, capture the transaction and report the conflict"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// before undoing, verify the recorded target still matches the live structure\nif _, ok := currentJSON[recordedKey]; !ok { return errors.New(\"undo target changed; refresh required\") }","typeGuard":"func isUndoConflict(err error) bool { return strings.Contains(err.Error(), \"conflicts with a subsequent edit\") }","tryCatchPattern":"if err := undo(); err != nil {\n    if isUndoConflict(err) {\n        clearUndoStack(); refreshDocument() // stack no longer reversible\n        return nil\n    }\n    return err\n}","preventionTips":["Avoid interleaving structural edits (deleting fields/rows) between the edit and its undo","Do not manually edit attribute-view JSON storage","Resolve sync merges before undoing"],"tags":["undo","conflict","state-mismatch"],"backgroundTag":"invalid-state-transition","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"}