Hmbown/CodeWhale · error

permission rule index changed before removal

Error message

permission rule index changed before removal

What it means

remove_permission_rule_item checks that the requested index still exists in the ArrayOfTables just before splicing it out. Even though the removal token was verified moments earlier under the lock, this guard defends against the index having drifted relative to the edited document item.

Solutions

  1. Reload with load_permissions_snapshot and retry with a fresh index and token
  2. Remember the API is 0-based: pass index-1 for what displays as rule N in listings that number from 1
  3. Check the current rule count (snap.permissions.rules.len()) before calling remove

Example fix

// before
remove_permission_rule(None, 3, &token)?; // only 3 rules (0..=2)

// after
let snap = load_permissions_snapshot(None)?;
if 3 < snap.permissions.rules.len() {
    remove_permission_rule(None, 3, &snap.removal_tokens[3])?;
}
Defensive patterns

Strategy: validation

Validate before calling

let snap = load_permissions_snapshot(config_path)?;
if index >= snap.permissions.rules.len() {
    return Err(anyhow::anyhow!("index {} out of range; {} rules", index, snap.permissions.rules.len()));
}

Type guard

fn rule_index_in_range(snap: &PermissionsSnapshot, index: usize) -> bool {
    index < snap.permissions.rules.len()
}

Try / catch

match remove_permission_rule(path, index, &token) {
    Err(e) if e.to_string().contains("index changed before removal") => {
        let snap = load_permissions_snapshot(path)?;
        if index < snap.removal_tokens.len() {
            remove_permission_rule(path, index, &snap.removal_tokens[index])?
        }
    }
    other => other?,
}

Prevention

When it happens

Trigger: remove_permission_rule with an index >= number of [[rules]] tables in the document item, typically from a stale snapshot whose index no longer exists because the file shrank or was restructured between listing and removal.

Common situations: Removing the last rule after another process already removed it; the user hand-deleted rules while the removal was pending; index arithmetic (1-based vs 0-based) confusion leading to an out-of-range index.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/6c9a942505b8358a. Report an issue: GitHub.

Appendix: source

Thrown at crates/config/src/lib.rs:6355

        }
        toml_edit::Item::Value(value) => {
            let Some(rules) = value.as_array_mut() else {
                bail!("`rules` in permissions.toml must be an array");
            };
            rules.push(toml_edit::Value::InlineTable(permission_rule_inline_table(
                rule,
            )));
            Ok(())
        }
        _ => bail!("`rules` in permissions.toml must be an array"),
    }
}

fn remove_permission_rule_item(item: &mut toml_edit::Item, index: usize) -> Result<Option<String>> {
    match item {
        toml_edit::Item::ArrayOfTables(rules) => {
            if index >= rules.len() {
                bail!("permission rule index changed before removal");
            }
            let file_header = if index == 0 {
                rules
                    .get(index)
                    .and_then(|rule| rule.decor().prefix())
                    .and_then(toml_edit::RawString::as_str)
                    .map(str::to_owned)
            } else {
                None
            };
            rules.remove(index);
            if let Some(header) = file_header.as_deref()
                && let Some(next_rule) = rules.get_mut(0)
            {
                let next_prefix = next_rule
                    .decor()
                    .prefix()
                    .and_then(toml_edit::RawString::as_str)

View on GitHub (pinned to 73e0f67d83)