Hmbown/CodeWhale · error

fleet task '{}' metadata.coordination_contracts must contain

Error message

fleet task '{}' metadata.coordination_contracts must contain only strings

What it means

One element of the `metadata.coordination_contracts` array is not a string — `value.as_str()` returned None for a number, boolean, null, or nested array/table. The field accepts only an array of strings; anything else is rejected before length or content checks run.

Source

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

    let Some(value) = task_spec.metadata.get("coordination_contracts") else {
        return Ok(Vec::new());
    };
    let Some(values) = value.as_array() else {
        bail!(
            "fleet task '{}' metadata.coordination_contracts must be an array of strings",
            task_spec.id
        );
    };
    if values.len() > 16 {
        bail!(
            "fleet task '{}' metadata.coordination_contracts accepts at most 16 entries",
            task_spec.id
        );
    }
    let mut contracts = Vec::new();
    for value in values {
        let Some(value) = value.as_str() else {
            bail!(
                "fleet task '{}' metadata.coordination_contracts must contain only strings",
                task_spec.id
            );
        };
        let value = value.trim();
        if value.is_empty()
            || value.chars().count() > 128
            || value.chars().any(|ch| matches!(ch, '\0' | '\r' | '\n'))
        {
            bail!(
                "fleet task '{}' coordination contracts must be one non-empty line of at most 128 characters",
                task_spec.id
            );
        }
        if !contracts.iter().any(|contract| contract == value) {
            contracts.push(value.to_string());
        }
    }

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Quote every entry in TOML and ensure JSON emits only strings.
  2. Omit null/empty optional entries at generation time instead of serializing them.
  3. After fixing, re-run spec validation to catch any follow-on content errors (length, newlines).

Example fix

# before
[metadata]
coordination_contracts = ["ledger", 42, true]

# after
[metadata]
coordination_contracts = ["ledger", "counter", "flag"]
Defensive patterns

Strategy: type-guard

Validate before calling

fn contracts_all_strings(values: &[serde_json::Value]) -> bool {
    values.iter().all(|v| v.is_string())
}

Type guard

fn as_string_array(value: &serde_json::Value) -> Option<&Vec<String>> {
    value.as_array()?.iter().all(|v| v.is_string()).then(|| {
        value.as_array().unwrap().iter().map(|v| v.as_str().unwrap().to_string()).collect::<Vec<_>>()
    })
}

Prevention

When it happens

Trigger: TOML like `coordination_contracts = ["ledger", 42]` or `["ledger", true]`; JSON with `["ledger", null]`; a generator mixing typed values into the list.

Common situations: Unquoted numerals in TOML lists; optional entries serialized as null instead of omitted; heterogeneous data pasted from application config.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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