Hmbown/CodeWhale · error · anyhow::Error
persistent allow rules must be scoped to a workspace
Error message
persistent allow rules must be scoped to a workspace
What it means
append_allow_rules (crates/config/src/lib.rs:5261) refuses to persist an allow rule whose workspace field is missing or fails codewhale_execpolicy::normalize_workspace_scope. Persistent allow grants must be scoped to one workspace so a UI bug can never write a global, unscoped allow. Session-scoped rules bypass this path entirely.
Source
Thrown at crates/config/src/lib.rs:5261
}
/// 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");
}
if let Some(path) = rule.path.as_deref()
&& codewhale_execpolicy::normalize_workspace_relative_path(path, &workspace)
.is_none_or(|path| path.is_empty())
{
bail!("persistent path allow rules must stay within the workspace");
}View on GitHub (pinned to 0c42157ee5)
Solutions
- Set rule.workspace to the absolute path of the workspace the decision was made in before appending
- Use the same construction path as the interactive permission prompt (it fills workspace automatically)
- Pre-validate with normalize_workspace_scope and surface a form error instead of hitting the bail
Example fix
// before
let rule = ToolAskRule { action: PermissionAction::Allow, tool: tool.clone(), workspace: None, /* ... */ };
config.append_allow_rules(&[rule])?; // -> must be scoped to a workspace
// after
let rule = ToolAskRule { action: PermissionAction::Allow, tool: tool.clone(), workspace: Some(workspace_root.display().to_string()), /* ... */ };
config.append_allow_rules(&[rule])?; Defensive patterns
Strategy: validation
Validate before calling
use codewhale_execpolicy::normalize_workspace_scope;
let Some(ws) = rule.workspace.as_deref().and_then(normalize_workspace_scope) else {
/* refuse before append: fill rule.workspace with the current workspace root */
unreachable!()
}; Type guard
fn is_persistable_allow_rule(rule: &ToolAskRule) -> bool {
rule.action == PermissionAction::Allow
&& rule.workspace.as_deref().and_then(normalize_workspace_scope).is_some()
} Try / catch
if let Err(e) = config.append_allow_rules(&[rule]) {
if e.to_string().contains("scoped to a workspace") { /* re-prompt with workspace filled */ }
else { return Err(e); }
} Prevention
- Always populate workspace from the session's workspace root when persisting approvals
- Never hand-construct persistent allow rules without running the validation set first
- Keep blanket 'always allow tool' decisions session-scoped
When it happens
Trigger: Constructing a ToolAskRule with action=Allow and workspace=None (or an empty/malformed workspace value) and passing it to append_allow_rules; typically a caller that builds rules by hand instead of via the permission-prompt flow.
Common situations: Custom UI/automation persisting 'always allow' decisions without capturing the current workspace root, deserialized rules from another machine losing the workspace field, refactors that drop the field.
Related errors
- 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
- fleet task '{}' is write-capable but declares no workspace.w
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/f8c147692ab17766.
Report an issue: GitHub.