Hmbown/CodeWhale · error

Fleet task selects member whose role is , but worker.role…

Error message

Fleet task {} selects member {:?} whose role is {:?}, but worker.role names a different posture {:?}; a task has one posture — drop worker.role or select a member with that role

What it means

A Fleet task both selects an explicit member and carries a legacy worker.role label naming a different posture. A task has exactly one posture; freeze_fleet_task_members rejects the contradiction instead of silently letting the write-capable legacy label widen the selection (issue #5945 context).

Solutions

  1. Drop the worker.role field from the task, keeping the member selection.
  2. Change the member selection to a member whose role matches the worker.role label.
  3. Update worker.role to the selected member's actual role.

Example fix

// before
"worker": {"member": "agent-1", "role": "impl"}  // agent-1 is a reviewer
// after
"worker": {"member": "agent-1"}
Defensive patterns

Strategy: validation

Validate before calling

fn posture_conflict(task: &Task, profile: &AgentProfile) -> bool { task.worker.as_ref().and_then(|w| w.role.as_deref()).map(|r| canonical_public_role_name(r) != profile.role).unwrap_or(false) }

Prevention

When it happens

Trigger: freeze_fleet_task_members where task.worker has both a member selector and a role label whose canonical_public_role_name differs from the selected member's role in the frozen snapshot.

Common situations: Editing a task's member without clearing the old role label; copying a task and swapping the member but not the role; older specs where the role field carried meaning it no longer does.

Understand the failure class

Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.

Related errors


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

Appendix: source

Thrown at crates/tui/src/fleet/worker_runtime.rs:228

        };

        if let Some(profile) = selected {
            validate_selected_member_model(task, profile)?;
            let snapshot = FrozenFleetMember::from_profile(profile);
            // A task carries exactly one posture. When an explicit member
            // selector is present, `worker.role` may only restate that
            // member's role (any casing or legacy alias); naming a different
            // posture is an authoring error, not a tie to arbitrate later.
            // Failing closed here is what keeps the launch-time resolver
            // honest: a read-only label can never widen to a member's write
            // authority, and a member's read-only slot can never be widened by
            // a write-capable label (#5945).
            if explicit_selector.is_some()
                && let Some(label) = legacy_role_selector.as_deref()
            {
                let label = canonical_public_role_name(label);
                if label != snapshot.role {
                    bail!(
                        "Fleet task {} selects member {:?} whose role is {:?}, but worker.role names a different posture {:?}; a task has one posture — drop worker.role or select a member with that role",
                        task.id,
                        profile.id,
                        snapshot.role,
                        label
                    );
                }
            }
            task.metadata.insert(
                FROZEN_FLEET_MEMBER_METADATA_KEY.to_string(),
                serde_json::to_value(&snapshot)?,
            );
            let worker = task.worker.as_mut().expect("worker checked above");
            worker.agent_profile = Some(format!("member:{}", profile.id));
            if explicit_selector.is_none() {
                worker.role = Some(snapshot.role.clone());
            } else if let Some(role) = worker.role.as_mut() {
                *role = canonical_public_role_name(role.trim());

View on GitHub (pinned to 73e0f67d83)