Hmbown/CodeWhale · error · anyhow::Error

Fleet authority fingerprint

Error message

Fleet authority fingerprint `{expected}` is not a form this build understands; refusing the spawn rather than launching an unverified child

What it means

verify_fleet_authority_input parses a Fleet authority fingerprint string of the form "v1;key=value;..." and requires at least 8 key=value fields. If the fingerprint does not start with "v1;" or has fewer than 8 fields, the build refuses to spawn the child agent rather than launching it with an unverifiable authority receipt. This is a fail-loud guard against forged or malformed authority data.

Solutions

  1. Regenerate the Fleet authority fingerprint with the current build so it uses the supported v1 format with all 8+ fields
  2. Verify the full fingerprint string was copied — check for truncation (missing trailing fields)
  3. Check that both parent and child tooling are the same version and share one fingerprint format
  4. Do not hand-edit the fingerprint; re-issue it through the fleet authority issuance path

Example fix

// before: truncated receipt
let fp = "v1;depth=2;allow=read";
// after: full v1 receipt with all required fields
let fp = "v1;depth=2;allow=read;deny=write;budget=...;model=...;scope=...;issuer=...;ts=...";
Defensive patterns

Strategy: validation

Validate before calling

fn is_supported_fingerprint(fp: &str) -> bool {
    fp.starts_with("v1;") && fp.split(';').filter(|p| p.contains('=')).count() >= 8
}

Try / catch

match spawn_child(&fp, &input) {
    Err(e) if e.to_string().contains("not a form this build understands") => {
        let fp = reissue_fingerprint(); // regenerate with current build
        spawn_child(&fp, &input)
    }
    other => other,
}

Prevention

When it happens

Trigger: Spawning a sub-agent whose fleet authority fingerprint string is from an older/other version (missing v1 prefix), was truncated in transit or storage, or was hand-edited so fewer than 8 fields remain.

Common situations: Mixing builds where one writes a newer fingerprint format the other cannot parse, copy-paste truncation of the receipt in config, or a stale persisted authority token from a previous release.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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

Appendix: source

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

/// The fingerprint is produced by `ChildAuthority::fingerprint` and names every
/// field of the envelope. Only four of them survive into the spawn input as
/// distinct keys — `write_authority`, `max_depth`, `allowed_tools`,
/// `disallowed_tools` — and those are exactly the four this function can and
/// does verify. The remaining fields (`tools`, `network`, `shell`, `posture`)
/// are *derivations* the Fleet used to compute those four, so a divergence in
/// any of them shows up in one of the four; verifying the wire form is
/// therefore the stronger check, not the weaker one, because it is the value
/// the child is actually constructed from.
///
/// Fails closed in every ambiguous case: an unparseable fingerprint is a
/// refusal, not a pass.
fn verify_fleet_authority_input(expected: &str, input: &Value) -> Result<()> {
    let fields: std::collections::HashMap<&str, &str> = expected
        .split(';')
        .filter_map(|part| part.split_once('='))
        .collect();
    if !expected.starts_with("v1;") || fields.len() < 8 {
        return Err(anyhow!(
            "Fleet authority fingerprint `{expected}` is not a form this build understands; \
             refusing the spawn rather than launching an unverified child"
        ));
    }

    let listed = |key: &str| -> String {
        let mut values: Vec<String> = input
            .get(key)
            .and_then(Value::as_array)
            .map(|items| {
                items
                    .iter()
                    .filter_map(Value::as_str)
                    .map(str::to_string)
                    .collect()
            })
            .unwrap_or_default();
        values.sort();

View on GitHub (pinned to 73e0f67d83)