Hmbown/CodeWhale · error
refusing inconsistent permission removal at
Error message
refusing inconsistent permission removal at {} What it means
After remove_permission_rule edits the TOML document, it re-parses the generated body and asserts the result contains exactly one fewer rule than before. If the re-parse or count check fails, the library refuses to persist the edit, protecting permissions.toml from a corrupting or surprising write.
Solutions
- Inspect permissions.toml for unusual structure (multiple `rules` keys, odd comments between [[rules]] tables) and normalize it to a plain array of tables
- Re-run load_permissions_snapshot and remove the rule again from a fresh snapshot
- Restore permissions.toml from backup or regenerate it, then remove the rule via the API
Example fix
// before (hand-edited, ambiguous file) rules = [] [[rules]] tool = "bash" // after (normalized) [[rules]] tool = "bash"
Defensive patterns
Strategy: validation
Validate before calling
// pre-parse permissions.toml yourself; reject ambiguous structure before calling the API let raw = std::fs::read_to_string(&path)?; let parsed: PermissionsToml = toml::from_str(&raw)?; // fails on non-standard shapes
Type guard
fn rules_is_array(doc: &toml_edit::DocumentMut) -> bool {
matches!(doc.get("rules"), Some(toml_edit::Item::ArrayOfTables(_))
| Some(toml_edit::Item::Value(v)) if v.is_array())
} Try / catch
match remove_permission_rule(path, index, &token) {
Err(e) if e.to_string().contains("refusing inconsistent permission removal") => {
eprintln!("normalize permissions.toml structure, reload snapshot, retry");
}
other => other?,
} Prevention
- Keep permissions.toml as a plain [[rules]] array of tables; avoid hand-crafted comment headers and duplicate keys
- Round-trip the file through the library's own load/save rather than external formatters
- Back up permissions.toml before scripted bulk edits
When it happens
Trigger: remove_permission_rule where parse_generated_permissions of the serialized document yields a rule count that is not exactly (original - 1), e.g. the edit removed zero or multiple rules.
Common situations: A permissions.toml with unusual structure (rules declared in a shape the editor mishandles, duplicate `rules` keys, inline-table arrays with embedded header comments) causing the round-trip to drop or merge rules; a bug in the removal path after manual hand-editing of the file.
Understand the failure class
Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.
Related errors
- failed to parse permissions at
- `rules` in permissions.toml must be an array
- active profile is missing or malformed
- agent profile tools.posture= would widen permissions; use…
- Cannot export a non-string default_model without losing its…
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/7f369a21dcd83bce.
Report an issue: GitHub.
Appendix: source
Thrown at crates/config/src/lib.rs:6257
let mut document = parse_permissions_document(path, &raw)?;
let rules_item = document.get_mut("rules").with_context(|| {
format!(
"permissions at {} no longer contain a rules array",
quote_os_path(path)
)
})?;
let orphaned_header = remove_permission_rule_item(rules_item, index)?;
if let Some(header) = orphaned_header {
let trailing = format!(
"{header}{}",
document.trailing().as_str().unwrap_or_default()
);
document.set_trailing(trailing);
}
let body = document.to_string();
let persisted = parse_generated_permissions(path, &body)?;
if persisted.rules.len() + 1 != permissions.rules.len() {
bail!(
"refusing inconsistent permission removal at {}",
quote_os_path(path)
);
}
write_permissions_atomic(path, body.as_bytes())?;
Ok(rule)
})
}
fn load_sibling_permissions(config_path: &Path) -> Result<PermissionsToml> {
let permissions_path = checked_permissions_path_for_config_path(config_path)?;
let (_, _, permissions) = read_permissions_state(&permissions_path)?;
Ok(permissions)
}
fn read_permissions_state(path: &Path) -> Result<(bool, String, PermissionsToml)> {
let file_exists = checked_path_exists(path)?;
let raw = if file_exists {View on GitHub (pinned to 73e0f67d83)