Hmbown/CodeWhale · error

agent profile tools.posture= would widen permissions; use…

Error message

agent profile {} tools.posture={other:?} would widen permissions; use FleetProfile policy for grants

What it means

Agent profile TOML files may not widen permissions. tools.posture is accepted only as read-only (with the readonly/read_only aliases); any other posture value in an agent profile is rejected because granting postures belongs in FleetProfile policy, not per-agent profile files.

Solutions

  1. Change tools.posture to "read-only" (or "readonly"/"read_only") in the agent profile
  2. Remove the tools.posture line entirely if the default read-only posture is fine
  3. Grant broader tool access through FleetProfile policy instead of the agent profile

Example fix

// before
[tools]
posture = "full"

// after
[tools]
posture = "read-only"
# broader grants go in FleetProfile policy
Defensive patterns

Strategy: validation

Validate before calling

let posture = profile.get("tools").and_then(|t| t.get("posture")).and_then(|v| v.as_str());
if let Some(p) = posture {
    if !matches!(p, "read-only" | "readonly" | "read_only") {
        eprintln!("agent profiles only allow read-only posture, got {p:?}");
    }
}

Try / catch

match result {
    Err(e) if e.to_string().contains("would widen permissions") => {
        eprintln!("move the tools.posture grant into FleetProfile policy");
    }
    other => other?,
}

Prevention

When it happens

Trigger: Setting `tools.posture` in an agent profile TOML to anything other than the read-only variants — e.g. posture = "full", "default", "standard", or a typo like "read-only" with extra characters — then calling agent_profile_from_toml via the profile loader.

Common situations: Copying a FleetProfile policy section (with a broader posture) into an agent profile; assuming postures other than read-only are valid per-agent; misspelling the read-only value.

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/3d4797b7662cbf87. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/fleet/profile.rs:492

/// Profile ids remain untouched so an older file can still be addressed by
/// its saved id. Only the semantic role is migrated; every new receipt and UI
/// label derived from it therefore uses the canonical public token.
pub(crate) fn canonical_public_role_name(role: &str) -> String {
    super::role::public_role_label(role)
}

fn reject_permission_expansion(
    path: &Path,
    tools: Option<&AgentProfileTools>,
    permissions: Option<&AgentProfilePermissionsToml>,
) -> Result<()> {
    if let Some(posture) = tools
        .and_then(|tools| tools.posture.as_deref())
        .and_then(trimmed_non_empty)
    {
        match posture {
            "read-only" | "readonly" | "read_only" => {}
            other => bail!(
                "agent profile {} tools.posture={other:?} would widen permissions; use FleetProfile policy for grants",
                path.display()
            ),
        }
    }

    if let Some(permissions) = permissions {
        if permissions.allow_shell.unwrap_or(false) {
            bail!(
                "agent profile {} may not request allow_shell=true",
                path.display()
            );
        }
        if permissions.trust.unwrap_or(false) {
            bail!(
                "agent profile {} may not request trust=true",
                path.display()
            );

View on GitHub (pinned to 73e0f67d83)