libnyanpasu/clash-nyanpasu · error
clean-schema output rejected by domain model: {e}
Error message
clean-schema output rejected by domain model: {e} What it means
After transforming the legacy document into the clean schema, run_clean_schema round-trips the migrated mapping through the typed domain model nyanpasu_config::profile::Profiles. If the deserializer rejects the migrated value (unknown/invalid fields, malformed items, duplicate uids enforced by R13), this error is thrown. It signals the migration transform itself produced an invalid document, so nothing is written.
Source
Thrown at backend/tauri/src/core/migration/modules/profiles.rs:256
let raw = std::fs::read_to_string(&path)?;
let doc: Mapping =
serde_yaml::from_str(&raw).map_err(|e| anyhow::anyhow!("failed to parse profiles: {e}"))?;
if is_clean_schema(&doc) {
return Ok(());
}
// R15: backup first, then transform (D3: mandatory .bak)
let bak = path.with_extension("yaml.bak");
crate::core::migration::fs::atomic_write(&bak, raw.as_bytes())?;
let migrated = migrate_clean_schema(doc)?;
// Typed round-trip: the only accepted output is a document the new domain
// model can load AND validate (design §14.4). Duplicate uids are rejected
// here by the items deserializer (R13).
let profiles: nyanpasu_config::profile::Profiles =
serde_yaml::from_value(Value::Mapping(migrated))
.map_err(|e| anyhow::anyhow!("clean-schema output rejected by domain model: {e}"))?;
profiles
.validate()
.map_err(|errors| anyhow::anyhow!("clean-schema output failed validation: {errors:?}"))?;
let body = serde_yaml::to_string(&profiles)
.map_err(|e| anyhow::anyhow!("failed to serialize migrated profiles: {e}"))?;
let content = format!("# Profiles Config for Clash Nyanpasu\n\n{body}");
crate::core::migration::fs::atomic_write(&path, content.as_bytes())?;
Ok(())
}
fn rollback_clean_schema(ctx: &mut Ctx) -> anyhow::Result<()> {
let path = ctx.profiles_path();
let bak = path.with_extension("yaml.bak");
if !bak.exists() {
eprintln!("profiles.yaml.bak not found, nothing to roll back");
return Ok(());
}View on GitHub (pinned to f7dbce2997)
Solutions
- Inspect {e} to find the offending field/item, then fix the corresponding entry in the source profiles.yaml (e.g. deduplicate or assign unique uids, fill missing required fields) and re-run migration.
- Check whether the file was already partially migrated; restore the .yaml.bak and retry on the pristine legacy data.
- Verify the nyanpasu-config domain model version matches the migration code — version skew can make valid legacy output invalid.
- If it reproduces on legitimate data, report it as a migration-transform bug: the transform should never emit a document the domain model rejects.
Example fix
// before
let profiles: Profiles = serde_yaml::from_value(Value::Mapping(migrated))
.map_err(|e| anyhow::anyhow!("clean-schema output rejected by domain model: {e}"))?;
// after
let profiles: Profiles = serde_yaml::from_value(Value::Mapping(migrated))
.with_context(|| format!("clean-schema output rejected by domain model for {}: duplicate uid or invalid item", path.display()))?; Defensive patterns
Strategy: validation
Validate before calling
fn migrated_output_loads(migrated: &serde_yaml::Mapping) -> Result<(), String> {
let v = serde_yaml::Value::Mapping(migrated.clone());
match serde_yaml::from_value::<nyanpasu_config::profile::Profiles>(v) {
Ok(_) => Ok(()),
Err(e) => Err(format!("domain model round-trip failed: {e}")),
}
} Type guard
fn loads_into_domain_model(migrated: serde_yaml::Mapping) -> bool {
serde_yaml::from_value::<nyanpasu_config::profile::Profiles>(Value::Mapping(migrated)).is_ok()
} Try / catch
let profiles: Profiles = match serde_yaml::from_value(Value::Mapping(migrated)) {
Ok(p) => p,
Err(e) => {
eprintln!("migration transform bug: output rejected by domain model: {e}");
eprintln!("restore profiles.yaml.bak; report duplicate-uid/invalid-item data");
return Err(e.into());
}
}; Prevention
- Deduplicate item uids in legacy data before migrating (copy-pasted profile blocks are the usual source)
- Add unit tests that round-trip real-world legacy profiles through the transform and domain model
- Keep the migration transform and nyanpasu-config model in the same version lockstep
- Fill required item fields (uid, type, url) during transform rather than leaving nulls
When it happens
Trigger: The migrated Mapping contains a profiles document the domain model cannot deserialize: duplicate item uids, items with missing/invalid type or url fields, wrong data types after transform (e.g. string where u32 expected), or legacy fields that map to none of the domain variants.
Common situations: Legacy profiles with unusual or malformed entries (empty uid, duplicate uids from copy-pasted profile blocks, exotic item types) surviving the transform; a bug in the migration transform leaving required fields null; version skew where the domain model gained new constraints the legacy data violates.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- clean-schema output failed validation: {errors:?}
- unrecognized typed config migration state: existing {} is ne
- unsupported migration store schema version {}
- profiles.yaml failed validation: {errors:?}
- failed to parse profiles: {e}
AI-assisted analysis of libnyanpasu/clash-nyanpasu@f7dbce2997 (2026-09-08).
Data as JSON: /api/errors/38a6cbd4f2df5eda.
Report an issue: GitHub.