Hmbown/CodeWhale · error · anyhow::Error
permissions changed after they were listed; reload {} and re
Error message
permissions changed after they were listed; reload {} and retry What it means
remove_permission_rule (crates/config/src/lib.rs:6101) bails with 'permissions changed after they were listed' when the resolved permissions.toml no longer exists at removal time. The compare-and-remove flow (snapshot -> token -> remove) assumes the file you listed still exists; deleting or renaming it between list and remove trips this guard instead of writing a fresh file from stale state.
Source
Thrown at crates/config/src/lib.rs:6101
})
}
/// Remove one zero-based permission rule if `expected_token` still describes
/// that exact index in the current file.
///
/// The file is re-read only after acquiring the same adjacent lock used by
/// append operations. This makes the token check and atomic replacement one
/// transaction, preventing stale list views from deleting a different rule.
pub fn remove_permission_rule(
config_path: Option<PathBuf>,
index: usize,
expected_token: &str,
) -> Result<ToolAskRule> {
let path = resolve_permissions_path(config_path)?;
config_document::with_config_write_lock(&path, |path| {
let (file_exists, raw, permissions) = read_permissions_state(path)?;
if !file_exists {
bail!(
"permissions changed after they were listed; reload {} and retry",
quote_os_path(path)
);
}
let rule = permissions.rules.get(index).cloned().with_context(|| {
format!(
"permission rule {} no longer exists in {}; list rules again",
index + 1,
quote_os_path(path)
)
})?;
let current_token = permission_removal_token(path, &raw, index);
if current_token != expected_token {
bail!(
"permissions changed after they were listed; reload {} and retry",
quote_os_path(path)
);
}View on GitHub (pinned to 0c42157ee5)
Solutions
- Re-run load_permissions_snapshot; a Missing file_state means there is nothing to remove — refresh the view
- If rules still matter, recreate permissions.toml by appending rules through the normal flow, then list and remove
- Serialize permission mutations through one surface so the file is not deleted under an active snapshot
Example fix
// before
let snap = load_permissions_snapshot(None)?;
// ... permissions.toml gets deleted here ...
remove_permission_rule(None, 0, &snap.removal_tokens[0])?; // -> changed after listed
// after
let snap = load_permissions_snapshot(None)?;
if snap.file_state == PermissionsFileState::Missing { /* nothing to remove; refresh UI */ } Defensive patterns
Strategy: retry
Validate before calling
let snap = load_permissions_snapshot(None)?;
if snap.file_state == PermissionsFileState::Missing {
// nothing to remove; refresh the view instead of calling remove_permission_rule
} Try / catch
match remove_permission_rule(None, idx, tok) {
Ok(rule) => { /* removed */ }
Err(e) if e.to_string().contains("permissions changed after they were listed") => {
let snap = load_permissions_snapshot(None)?; // reload, then retry with fresh tokens
}
Err(e) => return Err(e),
} Prevention
- Always branch on file_state from the snapshot before attempting removal
- Refresh permissions views when the file disappears
- Route all permission-file mutations through one owner process
When it happens
Trigger: Calling remove_permission_rule(config_path, index, token) after permissions.toml was deleted, moved, or never existed (snapshot taken from a Missing file state, then remove attempted anyway).
Common situations: User or a cleanup script deletes permissions.toml while a permissions UI is open; two sessions racing where one removes the file and the other tries to remove a rule from it.
Related errors
- persistent allow rules must be scoped to a workspace
- persistent command allow rules must use exact matching
- persistent allow rules must match an exact command or path
- persistent command allow rules must not be empty
- persistent path allow rules must stay within the workspace
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/bcfc4923cc0f73b4.
Report an issue: GitHub.