{"record":{"id":"1879009f101d263a","repo":"Hmbown/CodeWhale","slug":"a-pinned-task-provider-requires-an-explicit-model","errorCode":null,"errorMessage":"A pinned task provider requires an explicit model","messagePattern":"A pinned task provider requires an explicit model","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/task_manager.rs","lineNumber":1687,"sourceCode":"\n    /// Enqueue using a preallocated id. This is crate-visible only for the\n    /// model tool's register-before-work transaction.\n    pub(crate) async fn add_task_with_id(\n        &self,\n        req: NewTaskRequest,\n        task_id: String,\n    ) -> Result<TaskRecord> {\n        let prompt = req.prompt.trim().to_string();\n        if prompt.is_empty() {\n            bail!(\"Task prompt cannot be empty\");\n        }\n        if (req.model_provider.is_some() || req.model_provider_id.is_some())\n            && req\n                .model\n                .as_deref()\n                .is_none_or(|model| model.trim().is_empty())\n        {\n            bail!(\"A pinned task provider requires an explicit model\");\n        }\n        // The worker runs this same projection when it opens the task's\n        // thread. Running it here as well refuses an unknown mode or posture\n        // at the boundary the request crossed, instead of after the task has\n        // sat in the durable queue and a worker has claimed it.\n        crate::runtime_policy::RuntimePolicyProjection::from_request(\n            req.mode\n                .as_deref()\n                .filter(|mode| !mode.trim().is_empty())\n                .unwrap_or(&self.cfg.default_mode),\n            req.permission_posture.as_deref(),\n            req.auto_approve,\n        )?;\n        validate_preallocated_task_id(&task_id)?;\n\n        let task = TaskRecord {\n            schema_version: CURRENT_TASK_SCHEMA_VERSION,\n            // 16 random hex chars (was 8; ~60 bits of entropy once UUIDv4's","sourceCodeStart":1669,"sourceCodeEnd":1705,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/task_manager.rs#L1669-L1705","documentation":"When a NewTaskRequest pins a provider (model_provider or model_provider_id set), the request must also name an explicit model. The worker projects runtime policy with a concrete model, so an ambiguous provider-only request is refused at admission rather than failing later after the task sat in the durable queue.","triggerScenarios":"Calling add_task_with_id with model_provider/model_provider_id set but model None or empty/whitespace after trim.","commonSituations":"Automation rules that pin a provider but forget the model field; UI state where the model selector was cleared but the provider persisted; config templates carrying provider-only overrides.","solutions":["Set req.model to a concrete model name whenever req.model_provider/model_provider_id is set","Clear the provider fields if the intent is to use the default provider/model resolution","Validate the (provider, model) pair in the caller/UI before submitting the request","Ensure templates/config migrations that add a provider also carry a model"],"exampleFix":"// before\nlet req = NewTaskRequest { model_provider: Some(\"openai\"), model: None, .. };\n// after\nlet req = NewTaskRequest {\n    model_provider: Some(\"openai\"),\n    model: Some(\"gpt-4o\".into()),\n    ..\n};","handlingStrategy":"validation","validationCode":"anyhow::ensure!(req.model_provider.is_none() && req.model_provider_id.is_none() || req.model.as_deref().map_or(false, |m| !m.trim().is_empty()), \"pinned provider needs an explicit model\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Treat (provider, model) as one atomic selection in UI state","Keep config templates with provider pins carrying model pins too","Clear both provider fields together when reverting to defaults"],"tags":["rust","validation","tasks","configuration"],"backgroundTag":"missing-required-argument","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T11:17:16.035Z"}