Hmbown/CodeWhale · error
notification leaf has the wrong type or noncanonical value
Error message
notification leaf has the wrong type or noncanonical value
What it means
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").
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.
Example fix
// before [notifications] event_sound_min_interval_ms = "60000" # string instead of integer // after [notifications] event_sound_min_interval_ms = 60000
Defensive patterns
Strategy: validation
Validate before calling
// before calling edit_extras, ensure leaves have canonical TOML types:
// booleans unquoted, integers unquoted, paths as plain strings
fn leaf_matches(expected: &toml::Value, actual: &toml::Value) -> bool { expected == actual } Try / catch
match edit_extras(extras, key, value) {
Err(e) if e.to_string().contains("wrong type or noncanonical") => {
eprintln!("notifications leaf '{key}' has wrong TOML type; fix or remove it");
}
other => other?,
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
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
- a CNB access token is not configured in the Codewhale…
- account_agent_model_unconfigured
- active profile is missing or malformed
- advisor model ` ` resolved only to a custom provider kind…
- agent profile path is not a directory
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/87a6311cbad4005b.
Report an issue: GitHub.
Appendix: source
Thrown at crates/config/src/notifications.rs:442
let key = key.to_ascii_lowercase();
key == "notifications" || key.starts_with("notifications.")
}
pub fn edit_extras(
extras: &mut std::collections::BTreeMap<String, toml::Value>,
setting: NotificationSetting,
value: Option<toml::Value>,
) -> Result<()> {
if let Some(value) = &value {
let parsed = match (setting, value) {
(NotificationSetting::SoundFile, toml::Value::String(path)) => {
let update = NotificationConfigUpdate::SoundFile(PathBuf::from(path));
update.validate()?;
update
}
_ => NotificationConfigUpdate::parse(setting, &display_value(Some(value.clone())))?,
};
anyhow::ensure!(
parsed.value()?.as_ref() == Some(value),
"notification leaf has the wrong type or noncanonical value"
);
}
let mut updated = extras.clone();
if value.is_some() || updated.contains_key("notifications") {
let root = updated
.entry("notifications".into())
.or_insert_with(|| toml::Value::Table(toml::Table::new()));
let mut table = root
.as_table_mut()
.context("notifications must be a TOML table")?;
let segments = setting.key().split('.').collect::<Vec<_>>();
let (key, parents) = segments.split_last().expect("notification key");
let mut missing = false;
for parent in parents {
if value.is_none() && !table.contains_key(*parent) {
missing = true;View on GitHub (pinned to 73e0f67d83)