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
- Change tools.posture to "read-only" (or "readonly"/"read_only") in the agent profile
- Remove the tools.posture line entirely if the default read-only posture is fine
- 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
- Only use "read-only" for tools.posture in agent profiles
- Put broader tool grants in FleetProfile policy files, not per-agent profiles
- Never copy policy permission sections into agent profile TOMLs
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
- agent profile may not disable approval_required
- agent profile may not request allow_shell=true
- agent profile may not request trust=true
- agent profile path is not a directory
- duplicate agent profile id
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)