siyuan-note/siyuan · warning
database field undo conflicts with a subsequent edit
Error message
database field undo conflicts with a subsequent edit
What it means
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.
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
Defensive patterns
Strategy: try-catch
Validate before calling
// before undoing, verify the recorded target still matches the live structure
if _, ok := currentJSON[recordedKey]; !ok { return errors.New("undo target changed; refresh required") } Type guard
func isUndoConflict(err error) bool { return strings.Contains(err.Error(), "conflicts with a subsequent edit") } Try / catch
if err := undo(); err != nil {
if isUndoConflict(err) {
clearUndoStack(); refreshDocument() // stack no longer reversible
return nil
}
return err
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- attribute view custom color
- attribute view custom color
- block changed since conversion
- block swap undo state is unavailable
- box document ID [ ] is already in use
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/ada3ebe489d62525.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/model/attribute_view_field_changes.go:207
}
change.elements = map[string]*attributeViewFieldChange{}
change.beforeOrder, change.afterOrder = oldOrder, newOrder
for _, id := range append(append([]string(nil), oldOrder...), newOrder...) {
old, oldExists := oldElements[id]
newValue, newExists := newElements[id]
if child := diffAttributeViewFieldData(old, newValue, oldExists, newExists, container); child != nil {
change.elements[id] = child
}
}
if len(change.elements) == 0 {
return nil
}
change.before, change.after = nil, nil
return change
}
func (change *attributeViewFieldChange) apply(current any, exists, undo bool) (any, bool, error) {
conflict := fmt.Errorf("database field undo conflicts with a subsequent edit")
if change.members != nil {
object, ok := current.(map[string]any)
if !exists || !ok {
return nil, false, conflict
}
for _, key := range sortedAttributeViewFieldKeys(change.members) {
value, present := object[key]
replacement, keep, err := change.members[key].apply(value, present, undo)
if err != nil {
return nil, false, fmt.Errorf("%s: %w", key, err)
}
if keep {
object[key] = replacement
} else {
delete(object, key)
}
}
return object, true, nilView on GitHub (pinned to 9f775e8a12)