Repository navigation
Description
Overview
When NodeService::move_children_to_parent hits an in-transaction version conflict, the error reports expected = 0, actual = 0. The conflict is detected in SqliteStore::move_children_to_parent_in_tx, which returns a plain anyhow error with the message "VERSION_CONFLICT: node '<id>' version mismatch (expected N)". The service then turns that back into an error by string-matching:
if let Some(rest) = msg.strip_prefix("VERSION_CONFLICT: node '") {
let node_id = rest.split('\'').next().unwrap_or("unknown");
NodeServiceError::version_conflict(node_id, 0, 0)
}This has two problems:
- The contract depends on the message text. Rewording the store's error silently turns a version conflict into a generic
QueryFailed. The daemon would then stop mapping it toAborted, and the frontend would no longer see it as an OCC conflict it can retry or roll back. - The versions are wrong. Both are reported as
0. The store knows the expected version, and the conflicting child's actual version can be read inside the same transaction.
The daemon currently maps every VersionConflict to Aborted no matter what versions it carries, so nothing breaks today. The problem is how fragile and misleading the error is.
Proposed Solution
Have the store report the conflict as a typed value instead of a formatted message. For example, return Ok(Err(VersionConflict { node_id, expected, actual })), following the update_node_with_version_check_in_tx pattern (Result<Result<Node, i64>>), or use a typed store error. The service then maps it to NodeServiceError::version_conflict(node_id, expected, actual) with real values, and the string matching is deleted.
Check for other store methods that signal OCC failures by message prefix, and convert them the same way if any exist.
Acceptance Criteria
-
move_children_to_parent_in_txreports a version conflict as a typed value carrying the node id, the expected version and the actual version - The service's
strip_prefix("VERSION_CONFLICT: …")parsing is removed -
NodeServiceError::VersionConflictfrommove_children_to_parentcarries the real expected and actual versions (tests assert them) - No other store path signals OCC conflicts through message text
Reference Files
packages/core/src/db/sqlite_store/nodes.rs:move_children_to_parent_in_txpackages/core/src/services/node_service/hierarchy.rs:move_children_to_parent(the error mapping)packages/core/src/db/sqlite_store/nodes.rs:update_node_with_version_check_in_tx(the existing typed-conflict pattern)
Related
- Came out of the code review of Commit move_children_to_parent's edge swap and version bumps in one transaction (closes #2950) #3029 (issue move_children_to_parent commits edge swap and version bump in separate transactions #2950). This behavior predates that PR, and changing it was outside that PR's scope.
Activity
malibio commentedon Sep 25, 2026
Frontend: 5293 passed (265 files)