Hmbown/CodeWhale · warning

shell commands are restricted by

Error message

shell commands are restricted by {}

What it means

Shell command execution is gated by a ShellAccessControl policy (ProjectConfig, Profile, Environment, ManagedConfig, Ambiguous). When the resolved policy denies shell access, the worker bails with the policy label so the user knows which control blocked the command. This is deliberate permission enforcement, not a bug.

Solutions

  1. Enable shell in the owning configuration (project config, profile, or environment) that the label names
  2. Check config.allow_shell() and the ShellAccessControl resolution for the thread before invoking shell tools
  3. If ManagedConfig blocks it, contact the administrator managing that config

Example fix

// before (fails under Profile policy with allow_shell=false)
runtime.run_shell(thread_id, "cargo test").await?;
// after
if runtime.shell_policy_allows(thread_id).await {
    runtime.run_shell(thread_id, "cargo test").await?;
}
Defensive patterns

Strategy: try-catch

Validate before calling

if !config.allow_shell() { return Err(anyhow!("shell disabled by policy")); }

Try / catch

match runtime.run_shell(thread_id, cmd).await {
    Err(e) if e.to_string().starts_with("shell commands are restricted by") => {
        eprintln!("Enable shell in {}: {}", e, hint);
    }
    other => other?,
}

Prevention

When it happens

Trigger: Invoking a shell tool on a thread whose effective shell policy resolves to Profile, Environment, or ManagedConfig while config.allow_shell() is false (or policy is Ambiguous with default-deny).

Common situations: Fresh checkout where the project config has shell disabled; enterprise-managed config restricting shell; profile selected without shell permission; ambiguous settings after config migration.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


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

Appendix: source

Thrown at crates/tui/src/runtime_threads.rs:11216

        #[cfg(test)]
        let env_ticket = crate::test_support::env_scope_ticket();
        tokio::task::spawn_blocking(move || {
            #[cfg(test)]
            let _membership = crate::test_support::join_env_scope(env_ticket);
            let control = config.allow_shell_control(
                config_path.as_deref(),
                config_profile.as_deref(),
                &workspace,
            );
            let denied = match control {
                ShellAccessControl::Unset | ShellAccessControl::RootConfig => false,
                ShellAccessControl::ProjectConfig | ShellAccessControl::Ambiguous => true,
                ShellAccessControl::Profile
                | ShellAccessControl::Environment
                | ShellAccessControl::ManagedConfig => !config.allow_shell(),
            };
            if denied {
                bail!("shell commands are restricted by {}", control.label());
            }
            Ok(())
        })
        .await
        .context("shell policy inspection worker failed")?
    }

    /// The sandbox policy an API-created job inherits — the same posture
    /// projection a turn applies to its shell calls, so a client terminal
    /// cannot run looser than the thread's own tools would.
    pub(crate) async fn thread_job_sandbox_policy(
        &self,
        thread: &ThreadRecord,
    ) -> crate::sandbox::SandboxPolicy {
        let policy = RuntimePolicyProjection::from_persisted(
            &thread.mode,
            thread.permission_posture.as_deref(),
            thread.auto_approve,

View on GitHub (pinned to 73e0f67d83)