{"record":{"id":"9745a74aa8ff9079","repo":"Hmbown/CodeWhale","slug":"fleet-task-task-id-worker-model-cannot-be-empty","errorCode":null,"errorMessage":"fleet task {task_id} worker.model cannot be empty","messagePattern":"fleet task (.+?) worker\\.model cannot be empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/fleet/task_spec.rs","lineNumber":213,"sourceCode":"    if trimmed != value || !trimmed.chars().all(is_worker_token_char) {\n        bail!(\n            \"fleet task {task_id} {field} must be a simple token, not a path or provider/model id\"\n        );\n    }\n    Ok(())\n}\n\nfn is_worker_token_char(ch: char) -> bool {\n    ch.is_ascii_alphanumeric() || matches!(ch, '-' | '_' | '.')\n}\n\nfn validate_worker_model(task_id: &str, value: Option<&str>) -> Result<()> {\n    let Some(value) = value else {\n        return Ok(());\n    };\n    let trimmed = value.trim();\n    if trimmed.is_empty() {\n        bail!(\"fleet task {task_id} worker.model cannot be empty\");\n    }\n    if trimmed != value\n        || !trimmed\n            .chars()\n            .all(|ch| ch.is_ascii_graphic() && !matches!(ch, '=' | '\\'' | '\"'))\n    {\n        bail!(\n            \"fleet task {task_id} worker.model must be a visible model id without whitespace or secrets\"\n        );\n    }\n    Ok(())\n}\n\n#[allow(clippy::too_many_arguments)]\npub fn write_fleet_artifact_ref(\n    workspace: &Path,\n    run_id: &FleetRunId,\n    task_id: &str,","sourceCodeStart":195,"sourceCodeEnd":231,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/fleet/task_spec.rs#L195-L231","documentation":"validate_worker_model checks a task's optional worker.model override. If the field is present it must be non-blank after trimming; an empty or whitespace-only model is rejected rather than treated as \"use the default\".","triggerScenarios":"A task with \"worker\": { \"model\": \"\" } or \"model\": \"   \". Omitting the key passes - the worker then uses the profile-resolved model.","commonSituations":"Templates that always emit worker.model; clearing a per-task model override by emptying the string instead of deleting the key; generators writing empty strings for unset optionals.","solutions":["Delete the blank worker.model key to fall back to the profile's model","Or set the intended model id, e.g. \"worker\": { \"model\": \"gpt-4o\" }","Configure your serializer to skip empty optional fields when emitting specs"],"exampleFix":"// before\n{ \"id\": \"t1\", \"instructions\": \"...\", \"worker\": { \"model\": \"\" } }\n\n// after\n{ \"id\": \"t1\", \"instructions\": \"...\", \"worker\": { \"model\": \"gpt-4o\" } }","handlingStrategy":"validation","validationCode":"fn worker_model_ok(value: Option<&str>) -> bool {\n    value.map_or(true, |v| !v.trim().is_empty())\n}","typeGuard":"function optionalModelOk(v: string | undefined): boolean {\n  return v === undefined || v.trim().length > 0;\n}","tryCatchPattern":"if let Err(err) = load_task_spec_document(&path) {\n    if err.to_string().contains(\"worker.model cannot be empty\") {\n        eprintln!(\"{path:?}: blank worker.model - set a model id or drop the key\");\n    }\n    return Err(err);\n}","preventionTips":["Delete the worker.model key to inherit the profile model instead of writing \"\"","Serialize unset optionals as absent keys, not empty strings","Run validate_task_spec_document on every generated spec before launch"],"tags":["fleet","task-spec","worker","model-id","validation"],"backgroundTag":"empty-required-field","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}