libnyanpasu/clash-nyanpasu · error
clean-schema output rejected by domain model
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.
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
- failed to parse profiles
- failed to serialize migrated profiles
- failed to serialize profiles
- profiles.yaml failed validation
AI-assisted analysis of libnyanpasu/clash-nyanpasu@f7dbce2997 (2026-09-08).
Data as JSON: /api/errors/38a6cbd4f2df5eda.
Report an issue: GitHub.
Appendix: 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)