Hmbown/CodeWhale · error

append_allow_rules only accepts action = "allow"

Error message

append_allow_rules only accepts action = "allow"

What it means

append_allow_rules persists tool permission rules, but only allow-grants: every rule must have action == PermissionAction::Allow. Any deny/ask rule passed to this boundary is rejected so a UI bug cannot persist an unscoped or wrongly typed grant.

Solutions

  1. Filter the input to rules with action == PermissionAction::Allow before calling
  2. Route deny/ask rules to their intended persistence path instead
  3. Fix the caller that built the rule list if Allow rules are arriving with the wrong action

Example fix

// before
config.append_allow_rules(&all_prompt_rules)?;
// after
let allows: Vec<_> = all_prompt_rules.iter().filter(|r| r.action == PermissionAction::Allow).cloned().collect();
config.append_allow_rules(&allows)?;
Defensive patterns

Strategy: validation

Validate before calling

if rules.iter().any(|r| r.action != PermissionAction::Allow) {
    return Err("append_allow_rules accepts only action = allow rules");
}

Type guard

fn is_allow_rule(r: &ToolAskRule) -> bool { r.action == PermissionAction::Allow }

Try / catch

match config.append_allow_rules(&rules) {
    Err(e) if e.to_string().contains("only accepts action") => eprintln!("filter to Allow rules before persisting"),
    other => other?,
}

Prevention

When it happens

Trigger: Calling append_allow_rules with a ToolAskRule whose action is not Allow — e.g. passing captured ask/deny records straight through from a permission prompt handler.

Common situations: A permission UI batching all displayed rules (including denials) into one persist call; reusing a generic rule-append API for policy rules; a refactor that changed the action enum on existing records.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


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

Appendix: source

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

    /// `permissions.toml` file.
    ///
    /// Existing comments and formatting are preserved. Exact duplicate rules
    /// are ignored, and the in-memory permissions snapshot is refreshed after
    /// a successful write.
    pub fn append_ask_rules(&mut self, rules: &[ToolAskRule]) -> Result<usize> {
        self.append_permission_rules(rules, PermissionAction::Ask)
    }

    /// Atomically append exact, repo-scoped allow rules to the sibling
    /// `permissions.toml` file.
    ///
    /// The caller is responsible for deciding which tool calls are eligible;
    /// this boundary rejects broad or incorrectly typed records so a UI bug
    /// cannot persist an unscoped allow grant.
    pub fn append_allow_rules(&mut self, rules: &[ToolAskRule]) -> Result<usize> {
        for rule in rules {
            if rule.action != PermissionAction::Allow {
                bail!("append_allow_rules only accepts action = \"allow\"");
            }
            let Some(workspace) = rule
                .workspace
                .as_deref()
                .and_then(codewhale_execpolicy::normalize_workspace_scope)
            else {
                bail!("persistent allow rules must be scoped to a workspace");
            };
            if rule.command.is_some() && !rule.command_exact {
                bail!("persistent command allow rules must use exact matching");
            }
            if rule.command.is_none() && rule.path.is_none() {
                bail!("persistent allow rules must match an exact command or path");
            }
            if let Some(command) = rule.command.as_deref()
                && command.trim().is_empty()
            {
                bail!("persistent command allow rules must not be empty");

View on GitHub (pinned to 73e0f67d83)