Hmbown/CodeWhale · error

fleet authority mismatch at the spawn boundary: the receipt

Error message

fleet authority mismatch at the spawn boundary: the receipt names {key}=`{expected_value}` but the child would be constructed with `{actual}`. Refusing the spawn — a Fleet ceiling that does not reach the runtime is not a ceiling.

What it means

At the spawn boundary, `verify_fleet_authority_input` compares the actual spawn input just built against the receipt's fingerprint for four fields: write (write ceiling), depth (max_depth), allow (allowed_tools list), deny (disallowed_tools list). A mismatch means the receipt authorizes a different child surface than the one about to launch, so the spawn fails closed (mapped to ToolError::permission_denied). The wire form is the source of truth because it is what the child is actually constructed from.

Source

Thrown at crates/tui/src/tools/subagent/mod.rs:9635

    let actual_write = input
        .get("write_authority")
        .and_then(Value::as_str)
        .unwrap_or("read_only");
    let actual_depth = input
        .get("max_depth")
        .and_then(Value::as_u64)
        .map(|depth| depth.to_string())
        .unwrap_or_default();

    for (key, actual) in [
        ("write", actual_write.to_string()),
        ("depth", actual_depth),
        ("allow", actual_allow),
        ("deny", listed("disallowed_tools")),
    ] {
        let expected_value = fields.get(key).copied().unwrap_or_default();
        if expected_value != actual {
            return Err(anyhow!(
                "fleet authority mismatch at the spawn boundary: the receipt names {key}=`{expected_value}` \
                 but the child would be constructed with `{actual}`. Refusing the spawn — a Fleet \
                 ceiling that does not reach the runtime is not a ceiling."
            ));
        }
    }
    Ok(())
}

// === Sub-agent Execution ===

/// Build the system prompt for a sub-agent.
///
/// Starts with the per-type prompt (`FleetRole::system_prompt`) and
/// appends a one-line role overlay when `assignment.role` is set. The
/// full role library — TOML overlays from `~/.deepseek/roles/`, the
/// `/roles` slash command, model overrides per role — lands in 0.6.7.
/// For 0.6.6 we just don't drop the role on the floor: the model sees

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Re-issue the receipt (re-run the Fleet/workflow step that mints it) after any policy or option change, so the fingerprint matches the current spawn input.
  2. Diff the four fields named by the error (write, depth, allow, deny) to find which one the spawn added, dropped, or reordered.
  3. Do not mutate allowed_tools/disallowed_tools/max_depth between receipt creation and spawn_workflow_task.
  4. Never reuse persisted receipts across builds whose defaults changed — regenerate them.

Example fix

// before: receipt minted, then tools mutated before spawn
request.allowed_tools = Some(vec!["read".into()]); // diverges from receipt
spawn_workflow_task(identity, request).await?; // permission_denied (Err 1218)

// after: mint receipt from the same request you spawn with
let receipt = authority.fingerprint_for(&request);
let identity = Identity { fleet_authority_fingerprint: Some(receipt), .. };
spawn_workflow_task(identity, request).await?;
Defensive patterns

Strategy: try-catch

Validate before calling

// Re-run the same comparison before spawn to fail with a clear diagnostic:
if let Some(expected) = identity.fleet_authority_fingerprint.as_deref() {
    verify_fleet_authority_input(expected, &input)
        .context("receipt/spawn divergence — re-issue the receipt after policy changes")?;
}

Type guard

fn spawn_matches_receipt(expected: &str, input: &serde_json::Value) -> bool {
    verify_fleet_authority_input(expected, input).is_ok()
}

Try / catch

match spawn_workflow_task(identity, request).await {
    Err(ToolError::PermissionDenied(msg)) if msg.contains("fleet authority mismatch") => {
        // fail-closed spawn: diff write/depth/allow/deny vs the receipt, re-mint, retry once
    }
    r => r?,
}

Prevention

When it happens

Trigger: spawn_workflow_task where request.allowed_tools / disallowed_tools / max_depth / write flag diverge from the receipt: policy edited after the receipt was minted, defaults applied between minting and spawn, or host-derived ceilings (e.g. network_tool=false) converted to deny lists the receipt didn't name.

Common situations: Editing a Fleet's allow/deny lists after issuing receipts; replaying old receipts against a build with new defaults; code paths that add or filter tools between receipt creation and spawn; stale identity records attached to retried tasks.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/f6f318148d9ca46b. Report an issue: GitHub.