{"record":{"id":"7e8c5a7e478ae99c","repo":"Hmbown/CodeWhale","slug":"fleet-task-task-id-tag-cannot-be-empty","errorCode":null,"errorMessage":"fleet task {task_id} tag cannot be empty","messagePattern":"fleet task (.+?) tag cannot be empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/fleet/task_spec.rs","lineNumber":368,"sourceCode":"    Ok(receipt)\n}\n\npub fn record_verification_receipt(\n    ledger: &FleetLedger,\n    workspace: &Path,\n    input: &FleetTaskVerificationInput,\n    verification: FleetTaskVerification,\n) -> Result<FleetReceipt> {\n    let receipt = prepare_verification_receipt(workspace, input, verification)?;\n    ledger.record_receipt(receipt.clone())?;\n    Ok(receipt)\n}\n\nfn validate_tags(task_id: &str, tags: &[String]) -> Result<()> {\n    let mut seen = BTreeSet::new();\n    for tag in tags {\n        if tag.trim().is_empty() {\n            bail!(\"fleet task {task_id} tag cannot be empty\");\n        }\n        if !seen.insert(tag) {\n            bail!(\"fleet task {task_id} has duplicate tag {tag}\");\n        }\n    }\n    Ok(())\n}\n\nfn validate_workspace_requirements(task: &FleetTaskSpec) -> Result<()> {\n    let Some(workspace) = &task.workspace else {\n        return Ok(());\n    };\n    let env = workspace.environment.as_ref();\n    for name in env\n        .into_iter()\n        .flat_map(|env| env.required.iter().chain(env.allowlist.iter()))\n    {\n        if name.trim().is_empty() {","sourceCodeStart":350,"sourceCodeEnd":386,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/fleet/task_spec.rs#L350-L386","documentation":"validate_tags walks a task's tags array and rejects any entry that is empty after trimming. Tags index tasks for filtering and reporting, so blank tags are refused; duplicate tags are refused by the adjacent check.","triggerScenarios":"A task with \"tags\": [\"\"] or \"tags\": [\" \", \"backend\"] - any single blank entry fails the whole spec.","commonSituations":"Tag lists built by string splitting where an empty segment slips in (\"a,,b\"); templates with placeholder empty tags; copy-paste leaving a stray comma-quoted empty element.","solutions":["Remove empty and whitespace-only entries from the task's tags array","Filter out blank segments when generating tags by splitting strings","While editing, also de-duplicate tags to avoid the sibling duplicate-tag error"],"exampleFix":"// before\n{ \"id\": \"t1\", \"instructions\": \"...\", \"tags\": [\"\", \"backend\"] }\n\n// after\n{ \"id\": \"t1\", \"instructions\": \"...\", \"tags\": [\"backend\"] }","handlingStrategy":"validation","validationCode":"fn tags_valid(tags: &[String]) -> bool {\n    tags.iter().all(|t| !t.trim().is_empty())\n        && tags.iter().collect::<std::collections::BTreeSet<_>>().len() == tags.len()\n}","typeGuard":"function tagsValid(tags: string[]): boolean {\n  return (\n    tags.every((t) => t.trim().length > 0) &&\n    new Set(tags).size === tags.length\n  );\n}","tryCatchPattern":"if let Err(err) = load_task_spec_document(&path) {\n    if err.to_string().contains(\"tag cannot be empty\") {\n        eprintln!(\"{path:?}: remove blank entries from the task's tags array\");\n    }\n    return Err(err);\n}","preventionTips":["Filter empty segments when building tags from split strings (\"a,,b\")","Remove placeholder empty tags from templates","De-duplicate tags in the same pass to dodge the sibling duplicate-tag error"],"tags":["fleet","task-spec","tags","validation","required-field"],"backgroundTag":"empty-required-field","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}