{"record":{"id":"52145d62b22e4769","repo":"libnyanpasu/clash-nyanpasu","slug":"failed-to-serialize-config-e","errorCode":null,"errorMessage":"failed to serialize config: {e}","messagePattern":"failed to serialize config: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/tauri/src/core/migration/modules/storage.rs","lineNumber":115,"sourceCode":"                std::fs::create_dir_all(parent)?;\n            }\n            let storage = Storage::try_new(&storage_path)\n                .map_err(|e| anyhow::anyhow!(\"failed to open storage: {e}\"))?;\n\n            storage\n                .set_item(\"hotkeys\", &hotkey_strings)\n                .map_err(|e| anyhow::anyhow!(\"failed to save hotkeys: {e}\"))?;\n\n            // Note: registration is intentionally NOT done here. This migration\n            // runs in a separate `migrate` subprocess with no Tauri app handle, so\n            // `Hotkey::update` would fail; `Hotkey::init` reads the migrated value\n            // from KV storage at app startup instead.\n            tracing::info!(\"migrated {} hotkeys to KV storage\", hotkey_strings.len());\n        }\n\n        config.remove(&hotkeys_key);\n        let new_config = serde_yaml::to_string(&config)\n            .map_err(|e| anyhow::anyhow!(\"failed to serialize config: {e}\"))?;\n        crate::core::migration::fs::atomic_write(&config_path, new_config.as_bytes())?;\n\n        Ok(())\n    }\n}\n\nfn current_revision() -> u64 {\n    STEPS.last().map(|step| step.revision()).unwrap_or_default()\n}\n","sourceCodeStart":97,"sourceCodeEnd":125,"githubUrl":"https://github.com/libnyanpasu/clash-nyanpasu/blob/f7dbce2997c633e484f54788035e770b3ee99773/backend/tauri/src/core/migration/modules/storage.rs#L97-L125","documentation":"This error is raised during hotkey-to-KV storage migration when serde_yaml cannot serialize the modified config map back to YAML before it is atomically written to disk. It wraps the underlying serde_yaml error, so the message includes the serializer's own diagnostics. It indicates the in-memory config value contains data that cannot be represented as YAML (rare, e.g. non-string map keys of unsupported types or an IO-derived value that fails serialization).","triggerScenarios":"Calling the hotkey migration step in run() after config.remove(&hotkeys_key) when serde_yaml::to_string(&config) fails on the loaded YAML mapping — typically because the config contains keys or values serde_yaml cannot serialize (e.g. non-string keys not representable in YAML 1.1, or invalid Unicode scalars in values).","commonSituations":"A user's hotkey config file was hand-edited or written by an older/buggy version producing YAML with unusual key types; a corrupted config file loaded into a mapping with values the serializer rejects; platform-specific hotkey strings with invalid characters injected into the map.","solutions":["Read the wrapped serde_yaml message in {e} to identify the offending key/value","Open the config YAML file at config_path and fix or remove the invalid key/value manually","Delete or rename the config file so the migration recreates a clean default (backup first)","Verify the config is parsed into a serde_yaml::Mapping/String-keyed map rather than a generic Value with non-string keys","Report a bug if stock config data triggers it — the migration should be lossless for valid YAML"],"exampleFix":"// before\nlet new_config = serde_yaml::to_string(&config)\n    .map_err(|e| anyhow::anyhow!(\"failed to serialize config: {e}\"))?;\n// after\nlet new_config = serde_yaml::to_string(&config).map_err(|e| {\n    tracing::error!(\"hotkey migration: serialize failed: {e}; config keys: {:?}\", config.keys().collect::<Vec<_>>());\n    anyhow::anyhow!(\"failed to serialize config: {e}\")\n})?;","handlingStrategy":"try-catch","validationCode":"fn is_yaml_serializable(v: &serde_yaml::Value) -> bool {\n    match v {\n        serde_yaml::Value::Mapping(m) => m.iter().all(|(k, val)| {\n            matches!(k, serde_yaml::Value::String(_) | serde_yaml::Value::Number(_) | serde_yaml::Value::Bool(_))\n                && is_yaml_serializable(val)\n        }),\n        serde_yaml::Value::Sequence(s) => s.iter().all(is_yaml_serializable),\n        serde_yaml::Value::Tagged(t) => is_yaml_serializable(&t.value),\n        _ => true,\n    }\n}","typeGuard":"fn has_string_keys(m: &serde_yaml::Mapping) -> bool {\n    m.keys().all(|k| matches!(k, serde_yaml::Value::String(_)))\n}","tryCatchPattern":"match serde_yaml::to_string(&config) {\n    Ok(yaml) => atomic_write(&config_path, yaml.as_bytes())?,\n    Err(e) => {\n        log::error!(\"config serialization failed: {e}; aborting migration without writing\");\n        return Err(anyhow::anyhow!(\"failed to serialize config: {e}\"));\n    }\n}","preventionTips":["Keep config maps string-keyed when loading into generic Values","Validate hand-edited YAML config files with a linter before the app reads them","Back up the config file before running migrations","Never inject raw non-UTF8 or exotic-typed data into the config map"],"tags":["yaml","serialization","migration","config"],"backgroundTag":"json-serialization-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"}