{"record":{"id":"fec97d5d5d9a41e0","repo":"libnyanpasu/clash-nyanpasu","slug":"clean-schema-output-failed-validation-errors","errorCode":null,"errorMessage":"clean-schema output failed validation: {errors:?}","messagePattern":"clean-schema output failed validation: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/tauri/src/core/migration/modules/profiles.rs","lineNumber":259,"sourceCode":"    if is_clean_schema(&doc) {\n        return Ok(());\n    }\n\n    // R15: backup first, then transform (D3: mandatory .bak)\n    let bak = path.with_extension(\"yaml.bak\");\n    crate::core::migration::fs::atomic_write(&bak, raw.as_bytes())?;\n\n    let migrated = migrate_clean_schema(doc)?;\n\n    // Typed round-trip: the only accepted output is a document the new domain\n    // model can load AND validate (design §14.4). Duplicate uids are rejected\n    // here by the items deserializer (R13).\n    let profiles: nyanpasu_config::profile::Profiles =\n        serde_yaml::from_value(Value::Mapping(migrated))\n            .map_err(|e| anyhow::anyhow!(\"clean-schema output rejected by domain model: {e}\"))?;\n    profiles\n        .validate()\n        .map_err(|errors| anyhow::anyhow!(\"clean-schema output failed validation: {errors:?}\"))?;\n\n    let body = serde_yaml::to_string(&profiles)\n        .map_err(|e| anyhow::anyhow!(\"failed to serialize migrated profiles: {e}\"))?;\n    let content = format!(\"# Profiles Config for Clash Nyanpasu\\n\\n{body}\");\n    crate::core::migration::fs::atomic_write(&path, content.as_bytes())?;\n    Ok(())\n}\n\nfn rollback_clean_schema(ctx: &mut Ctx) -> anyhow::Result<()> {\n    let path = ctx.profiles_path();\n    let bak = path.with_extension(\"yaml.bak\");\n    if !bak.exists() {\n        eprintln!(\"profiles.yaml.bak not found, nothing to roll back\");\n        return Ok(());\n    }\n    let raw = std::fs::read(&bak)?;\n    crate::core::migration::fs::atomic_write(&path, &raw)\n}","sourceCodeStart":241,"sourceCodeEnd":277,"githubUrl":"https://github.com/libnyanpasu/clash-nyanpasu/blob/f7dbce2997c633e484f54788035e770b3ee99773/backend/tauri/src/core/migration/modules/profiles.rs#L241-L277","documentation":"After successful deserialization into the domain model, run_clean_schema calls Profiles::validate() to enforce cross-field invariants (design §14.4). If validate() returns errors, the migration aborts with 'clean-schema output failed validation: {errors:?}' and the original file is left untouched (the .bak may already have been written).","triggerScenarios":"The migrated document deserializes fine but violates domain invariants checked by Profiles::validate() — e.g. structurally valid items with semantically invalid combinations, dangling references, or constraint violations not expressible in serde.","commonSituations":"Legacy data that serde accepts but the new model's semantic rules reject (e.g. duplicate references after transform, ordering/current-pointer inconsistencies); a migration-transform bug producing semantically inconsistent output.","solutions":["Read the {errors:?} list to identify which invariants failed, and correct the corresponding entries in the source profiles.yaml before re-running migration.","Restore from profiles.yaml.bak and retry migration on pristine data to rule out a previously corrupted half-migration.","If validation fails on reasonable legacy data, treat it as a bug in the migration transform or in validate() and report with the errors list.","Add a pre-check running the same validate() on a dry-run migrated copy to surface issues before committing."],"exampleFix":"// before\nprofiles.validate()\n    .map_err(|errors| anyhow::anyhow!(\"clean-schema output failed validation: {errors:?}\"))?;\n// after\nprofiles.validate()\n    .with_context(|| format!(\"clean-schema validation failed for {} — restore .yaml.bak and fix source data\", path.display()))?;","handlingStrategy":"validation","validationCode":"if let Err(errors) = profiles.validate() {\n    return Err(anyhow::anyhow!(\"pre-commit validation failed: {errors:?}\"));\n} // run this on the in-memory Profiles before any serialization/write","typeGuard":"fn is_valid_profiles(p: &nyanpasu_config::profile::Profiles) -> bool {\n    p.validate().is_ok()\n}","tryCatchPattern":"if let Err(errors) = profiles.validate() {\n    eprintln!(\"clean-schema output failed validation: {errors:?}\");\n    eprintln!(\"profiles.yaml left untouched; restore .yaml.bak if needed\");\n    return Err(anyhow::anyhow!(\"validation failed: {errors:?}\"));\n}","preventionTips":["Dry-run the transform + validate() on a copy of the data before committing","Cover known legacy-data quirks with validate() unit tests","Surface the structured {errors:?} list to users instead of a generic failure","Keep domain invariants and the migration transform updated together"],"tags":["migration","profiles","validation","domain-model"],"backgroundTag":"schema-validation-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"}