{"record":{"id":"ce7b97f8586b5b90","repo":"libnyanpasu/clash-nyanpasu","slug":"legacy-mutation-may-have-non-reversible-side-effec","errorCode":null,"errorMessage":"legacy mutation may have non-reversible side effects and requires reconciliation: {error:#}","messagePattern":"legacy mutation may have non-reversible side effects and requires reconciliation: (.+?)","errorType":"exception","errorClass":"ClientError","httpStatus":null,"severity":"error","filePath":"backend/tauri/src/bridge/verge.rs","lineNumber":333,"sourceCode":"                    // (feat::patch_verge) would have reconciled against\n                    // before this commit ran. A reconcile failure here must\n                    // not undo the successful commit: report it the same way\n                    // the commit-phase failures above already do.\n                    managed\n                        .client\n                        .rebuild_running_config()\n                        .await\n                        .map_err(|error| {\n                            Self::legacy_mutation_partial(\n                                anyhow::anyhow!(format!(\"{error:#}\")),\n                                Some(error),\n                            )\n                        })?;\n                }\n                Ok(())\n            }\n            Err(error) => Err(Self::legacy_mutation_partial(\n                anyhow::anyhow!(format!(\"{error:#}\")),\n                Some(error),\n            )),\n        }\n    }\n\n    fn legacy_mutation_partial(error: anyhow::Error, source: Option<ClientError>) -> ClientError {\n        let message = format!(\n            \"legacy mutation may have non-reversible side effects and requires reconciliation: {error:#}\"\n        );\n        if let Some(ClientError::PartialCommit(partial)) = source {\n            return partial.with_legacy_state_uncertain(message).into();\n        }\n\n        let primary = ClientError::Anyhow(error);\n        PartialCommit::new(&primary, Vec::new(), Vec::new(), Vec::new())\n            .with_legacy_state_uncertain(message)\n            .into()\n    }","sourceCodeStart":315,"sourceCodeEnd":351,"githubUrl":"https://github.com/libnyanpasu/clash-nyanpasu/blob/f7dbce2997c633e484f54788035e770b3ee99773/backend/tauri/src/bridge/verge.rs#L315-L351","documentation":"Raised on the `Err(error)` branch of `apply_typed_config_patch_plan` inside `run_legacy_verge_mutation`: the typed configuration patch plan failed to apply. It is wrapped by `legacy_mutation_partial`, so the message always carries the prefix \"legacy mutation may have non-reversible side effects and requires reconciliation\", meaning the legacy store was already mutated by `mutate()` and partially restored/re-committed, leaving state possibly inconsistent with the typed config.","triggerScenarios":"Calling `patch_verge_config` (or any wrapper of `run_legacy_verge_mutation`) where the computed typed patch plan fails during application: a lock contention failure, an I/O failure writing the typed config, a commit finalize error, or a validation rejection inside `apply_typed_config_patch_plan`.","commonSituations":"Config file on disk became read-only or locked by another process; concurrent patch requests raced despite the update lock (e.g. multiple app instances); the patch plan referenced keys rejected by the typed layer; disk full during the commit write.","solutions":["Read the inner `{error:#}` chain to identify the apply failure (IO, lock, validation).","Check filesystem permissions/disk space for the app config directory and fix them.","Ensure only one app instance runs (the bridge serializes via an in-process lock only).","Re-run the patch; because the error flags legacy state uncertainty, compare the verge config against the intended patch and correct any drifted fields."],"exampleFix":"// before: fire-and-forget patch that ignores partial state\nspawn(patch_verge_config(patch));\n\n// after: await and reconcile on partial-commit failure\nif let Err(e) = client.patch_verge_config(patch).await {\n    log::warn!(\"patch failed, reconciling: {e:#}\");\n    let current = client.get_app_config().await?;\n    // verify/correct drifted legacy fields here\n}","handlingStrategy":"try-catch","validationCode":"// pre-flight checks before patching\nlet cfg_path = app_config_dir().join(\"verge.yaml\");\nanyhow::ensure!(cfg_path.parent().map(|p| p.exists()).unwrap_or(false), \"config dir missing\");\nlet meta = std::fs::metadata(&cfg_path)?;\nanyhow::ensure!(!meta.permissions().readonly(), \"verge config is read-only\");","typeGuard":null,"tryCatchPattern":"if let Err(e) = client.patch_verge_config(patch).await {\n    if e.to_string().contains(\"non-reversible side effects\") {\n        // verify on-disk state vs intended patch and repair drift\n        let actual = client.get_app_config().await?;\n        log::warn!(\"drift check needed after failed patch: {e:#}; actual={actual:?}\");\n    } else {\n        return Err(e.into());\n    }\n}","preventionTips":["Ensure writable config directory and sufficient disk space.","Run only one app instance; the update lock is in-process only.","Validate patches against the typed schema before applying.","After any failure, diff the committed config against intent to repair drift."],"tags":["rust","config","commit-failure","partial-commit"],"backgroundTag":"database-write-failed","analyzedSha":"f7dbce2997c633e484f54788035e770b3ee99773","analyzedAt":"2026-09-08T01:24:59.197Z","contentChangedAt":"2026-09-08T01:24:59.197Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}