{"record":{"id":"d34eb7fabc8c94bc","repo":"libnyanpasu/clash-nyanpasu","slug":"cannot-repair-typed-clash-config-before-split-lega","errorCode":null,"errorMessage":"cannot repair typed clash config before split_legacy_config has completed","messagePattern":"cannot repair typed clash config before split_legacy_config has completed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/tauri/src/core/migration/modules/typed_config.rs","lineNumber":118,"sourceCode":"    }\n\n    fn revision(&self) -> u64 {\n        2\n    }\n\n    fn introduced_in(&self) -> &'static Version {\n        &VERSION_2_0_0\n    }\n\n    fn name(&self) -> &'static str {\n        \"RepairClashConfigPath\"\n    }\n\n    fn run(&self, ctx: &mut Ctx) -> anyhow::Result<()> {\n        match typed_file_state(ctx)? {\n            TypedFileState::All => return Ok(()),\n            TypedFileState::None => {\n                bail!(\"cannot repair typed clash config before split_legacy_config has completed\")\n            }\n            TypedFileState::NeedsClashRepair => {}\n        }\n\n        let previous_typed_path = ctx.paths().app_config_dir().join(PREVIOUS_TYPED_CLASH_FILE);\n        let clash_config = if previous_typed_path.exists() {\n            read_yaml::<nyanpasu_config::clash::config::ClashConfig>(&previous_typed_path)\n                .context(\"failed to read previous typed clash config\")?\n        } else {\n            let legacy = read_legacy_verge(&ctx.nyanpasu_config_path())?;\n            let legacy_clash = read_legacy_clash_inputs(ctx)?;\n            let (_, _, clash_config) = typed_config_from_legacy_parts(&legacy, &legacy_clash)?;\n            clash_config\n        };\n\n        let clash_yaml =\n            serialize_yaml(&clash_config).context(\"failed to serialize repaired clash config\")?;\n        crate::core::migration::fs::atomic_write(&ctx.clash_config_path(), clash_yaml.as_bytes())","sourceCodeStart":100,"sourceCodeEnd":136,"githubUrl":"https://github.com/libnyanpasu/clash-nyanpasu/blob/f7dbce2997c633e484f54788035e770b3ee99773/backend/tauri/src/core/migration/modules/typed_config.rs#L100-L136","documentation":"The typed-config migration module inspects on-disk state via `typed_file_state` before repairing the typed clash config. If no migration artifacts exist at all (`TypedFileState::None`), the repair step cannot run because `split_legacy_config` — the step that produces the typed files — has not completed. It fails deliberately to enforce migration ordering.","triggerScenarios":"Running the `run` step of the typed-clash-config repair migration when the app config dir contains neither typed files nor legacy artifacts, i.e. `split_legacy_config` never ran (fresh install or missing prerequisite migration step).","commonSituations":"Manually deleting the config directory contents while keeping the migration registered; running migrations out of order after editing the migration list; restoring a partial backup that predates the split step.","solutions":["Ensure the `split_legacy_config` migration runs (and succeeds) before this repair step in the migration sequence.","If this is a fresh install with intentionally no config, skip the repair step rather than treating absent files as an error.","Restore the expected config files from backup so `typed_file_state` returns `All` or `NeedsClashRepair`."],"exampleFix":"// before\nmigrations.push(typed_config_repair); // runs even on fresh installs\n\n// after\nif typed_file_state(&ctx)? != TypedFileState::None {\n    migrations.push(typed_config_repair);\n}","handlingStrategy":"validation","validationCode":"let clash = paths.app_config_dir().join(\"clash-config.yaml\");\nif !clash.exists() && !paths.app_config_dir().join(\"config.yaml\").exists() {\n    // fresh install: skip repair step\n}","typeGuard":null,"tryCatchPattern":"match migration.run(&mut ctx) {\n    Err(e) if e.to_string().contains(\"split_legacy_config has completed\") => {\n        log::warn!(\"repair step skipped: prerequisites missing; running split first\");\n        split_legacy_config.run(&mut ctx)?;\n        migration.run(&mut ctx)?;\n    }\n    other => other?,\n}","preventionTips":["Keep the full migration chain registered and ordered; never run later repair steps standalone.","Do not manually delete config files expected by migration steps.","On fresh installs, short-circuit the repair step when the config dir is empty."],"tags":["migration","config","rust"],"backgroundTag":"invalid-state-transition","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"}