{"record":{"id":"39ea6a8203a7ac1b","repo":"Hmbown/CodeWhale","slug":"fleet-task-metadata-coordination-contracts-mu-39ea6a","errorCode":null,"errorMessage":"fleet task '{}' metadata.coordination_contracts must contain only strings","messagePattern":"fleet task '(.+?)' metadata\\.coordination_contracts must contain only strings","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/fleet/worker_runtime.rs","lineNumber":397,"sourceCode":"    let Some(value) = task_spec.metadata.get(\"coordination_contracts\") else {\n        return Ok(Vec::new());\n    };\n    let Some(values) = value.as_array() else {\n        bail!(\n            \"fleet task '{}' metadata.coordination_contracts must be an array of strings\",\n            task_spec.id\n        );\n    };\n    if values.len() > 16 {\n        bail!(\n            \"fleet task '{}' metadata.coordination_contracts accepts at most 16 entries\",\n            task_spec.id\n        );\n    }\n    let mut contracts = Vec::new();\n    for value in values {\n        let Some(value) = value.as_str() else {\n            bail!(\n                \"fleet task '{}' metadata.coordination_contracts must contain only strings\",\n                task_spec.id\n            );\n        };\n        let value = value.trim();\n        if value.is_empty()\n            || value.chars().count() > 128\n            || value.chars().any(|ch| matches!(ch, '\\0' | '\\r' | '\\n'))\n        {\n            bail!(\n                \"fleet task '{}' coordination contracts must be one non-empty line of at most 128 characters\",\n                task_spec.id\n            );\n        }\n        if !contracts.iter().any(|contract| contract == value) {\n            contracts.push(value.to_string());\n        }\n    }","sourceCodeStart":379,"sourceCodeEnd":415,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/fleet/worker_runtime.rs#L379-L415","documentation":"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.","triggerScenarios":"TOML like `coordination_contracts = [\"ledger\", 42]` or `[\"ledger\", true]`; JSON with `[\"ledger\", null]`; a generator mixing typed values into the list.","commonSituations":"Unquoted numerals in TOML lists; optional entries serialized as null instead of omitted; heterogeneous data pasted from application config.","solutions":["Quote every entry in TOML and ensure JSON emits only strings.","Omit null/empty optional entries at generation time instead of serializing them.","After fixing, re-run spec validation to catch any follow-on content errors (length, newlines)."],"exampleFix":"# before\n[metadata]\ncoordination_contracts = [\"ledger\", 42, true]\n\n# after\n[metadata]\ncoordination_contracts = [\"ledger\", \"counter\", \"flag\"]","handlingStrategy":"type-guard","validationCode":"fn contracts_all_strings(values: &[serde_json::Value]) -> bool {\n    values.iter().all(|v| v.is_string())\n}","typeGuard":"fn as_string_array(value: &serde_json::Value) -> Option<&Vec<String>> {\n    value.as_array()?.iter().all(|v| v.is_string()).then(|| {\n        value.as_array().unwrap().iter().map(|v| v.as_str().unwrap().to_string()).collect::<Vec<_>>()\n    })\n}","tryCatchPattern":null,"preventionTips":["Type the field as Vec<String> in any producer struct so serde cannot emit non-strings.","Skip null/None entries at generation instead of serializing them.","Quote every entry in TOML lists, including numerals."],"tags":["fleet","metadata","coordination","type-mismatch","rust"],"backgroundTag":"schema-validation-failed","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}