{"record":{"id":"87a6311cbad4005b","repo":"Hmbown/CodeWhale","slug":"notification-leaf-has-the-wrong-type-or-noncanonical-value","errorCode":null,"errorMessage":"notification leaf has the wrong type or noncanonical value","messagePattern":"notification leaf has the wrong type or noncanonical value","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/config/src/notifications.rs","lineNumber":442,"sourceCode":"    let key = key.to_ascii_lowercase();\n    key == \"notifications\" || key.starts_with(\"notifications.\")\n}\n\npub fn edit_extras(\n    extras: &mut std::collections::BTreeMap<String, toml::Value>,\n    setting: NotificationSetting,\n    value: Option<toml::Value>,\n) -> Result<()> {\n    if let Some(value) = &value {\n        let parsed = match (setting, value) {\n            (NotificationSetting::SoundFile, toml::Value::String(path)) => {\n                let update = NotificationConfigUpdate::SoundFile(PathBuf::from(path));\n                update.validate()?;\n                update\n            }\n            _ => NotificationConfigUpdate::parse(setting, &display_value(Some(value.clone())))?,\n        };\n        anyhow::ensure!(\n            parsed.value()?.as_ref() == Some(value),\n            \"notification leaf has the wrong type or noncanonical value\"\n        );\n    }\n    let mut updated = extras.clone();\n    if value.is_some() || updated.contains_key(\"notifications\") {\n        let root = updated\n            .entry(\"notifications\".into())\n            .or_insert_with(|| toml::Value::Table(toml::Table::new()));\n        let mut table = root\n            .as_table_mut()\n            .context(\"notifications must be a TOML table\")?;\n        let segments = setting.key().split('.').collect::<Vec<_>>();\n        let (key, parents) = segments.split_last().expect(\"notification key\");\n        let mut missing = false;\n        for parent in parents {\n            if value.is_none() && !table.contains_key(*parent) {\n                missing = true;","sourceCodeStart":424,"sourceCodeEnd":460,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/config/src/notifications.rs#L424-L460","documentation":"This error is thrown by edit_extras after parsing an existing notifications setting and re-reading its canonical value: if the round-tripped value does not equal the original config value, the leaf is deemed to have the wrong type or a noncanonical representation. It protects the config rewrite path from silently transforming values (e.g. a string where an integer is expected, or 1 vs \"1\").","triggerScenarios":"Calling edit_extras with an extras table containing a notifications leaf whose TOML type or textual form does not survive NotificationConfigUpdate::parse(...).value() round-trip equality — e.g. wrong type for the key (string instead of integer/boolean), a value in noncanonical form, or a custom path represented differently than the canonical string form.","commonSituations":"A user hand-edited the notifications table and changed a leaf's type (quoted a boolean, stringified a number); an external tool wrote the config in a noncanonical style; a version upgrade changed the canonical representation of a value.","solutions":["Restore the leaf to the expected type and canonical form for that key (booleans as true/false, integers unquoted, paths as plain strings).","Delete the malformed leaf and re-set it through the library's own update/apply path so it is written canonically.","Regenerate the notifications section from a known-good default config.","Check for recent hand edits or tool writes that changed the value's TOML type."],"exampleFix":"// before\n[notifications]\nevent_sound_min_interval_ms = \"60000\"  # string instead of integer\n// after\n[notifications]\nevent_sound_min_interval_ms = 60000","handlingStrategy":"validation","validationCode":"// before calling edit_extras, ensure leaves have canonical TOML types:\n// booleans unquoted, integers unquoted, paths as plain strings\nfn leaf_matches(expected: &toml::Value, actual: &toml::Value) -> bool { expected == actual }","typeGuard":null,"tryCatchPattern":"match edit_extras(extras, key, value) {\n    Err(e) if e.to_string().contains(\"wrong type or noncanonical\") => {\n        eprintln!(\"notifications leaf '{key}' has wrong TOML type; fix or remove it\");\n    }\n    other => other?,\n}","preventionTips":["Do not hand-edit typed values into strings (e.g. \"60000\" instead of 60000)","Let the library write config values so canonical form is guaranteed","Diff against a known-good config after external tools modify it"],"tags":["config","round-trip","canonicalization"],"backgroundTag":"schema-validation-failed","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}