{"record":{"id":"871baa7f90f12691","repo":"Hmbown/CodeWhale","slug":"at-least-one-thread-field-is-required","errorCode":null,"errorMessage":"At least one thread field is required","messagePattern":"At least one thread field is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/runtime_threads.rs","lineNumber":7876,"sourceCode":"        id: &str,\n        req: UpdateThreadRequest,\n        config_path: Option<&Path>,\n        config_profile: Option<&str>,\n    ) -> Result<ThreadRecord> {\n        // Keep policy publication ordered with this authorization decision.\n        let _config_admission = self.config_admission.read().await;\n        if req.archived.is_none()\n            && req.allow_shell.is_none()\n            && req.trust_mode.is_none()\n            && req.auto_approve.is_none()\n            && req.model.is_none()\n            && req.mode.is_none()\n            && req.permission_posture.is_none()\n            && req.title.is_none()\n            && req.system_prompt.is_none()\n            && req.workspace.is_none()\n        {\n            bail!(\"At least one thread field is required\");\n        }\n\n        if let Some(model) = req.model.as_ref()\n            && model.trim().is_empty()\n        {\n            bail!(\"model must not be empty\");\n        }\n        if let Some(mode) = req.mode.as_ref()\n            && mode.trim().is_empty()\n        {\n            bail!(\"mode must not be empty\");\n        }\n        if let Some(permission_posture) = req.permission_posture.as_ref()\n            && permission_posture.trim().is_empty()\n        {\n            bail!(\"permission_posture must not be empty\");\n        }\n        if let Some(workspace) = req.workspace.as_ref()","sourceCodeStart":7858,"sourceCodeEnd":7894,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/runtime_threads.rs#L7858-L7894","documentation":"The thread-update request validates that at least one field is actually set (`model`, `mode`, `permission_posture`, `title`, `system_prompt`, `workspace`, etc.). A request where every optional field is `None` is a no-op and is rejected instead of silently doing nothing.","triggerScenarios":"Calling the update-thread API with an entirely empty/default request struct — all `Option` fields left as `None` (e.g. deserialized from an empty JSON object `{}`).","commonSituations":"A PATCH-style HTTP client sends `{}` as the body; a frontend builds the request object conditionally and all conditions are false; a caller constructs `UpdateThreadRequest::default()`.","solutions":["Set at least one field on the request before calling update (e.g. `title: Some(...)` or `model: Some(...)`).","On the caller side, check the built request for all-empty fields and skip the API call entirely.","If the intent is a read, use the get-thread API instead of a no-op update."],"exampleFix":"// before\nlet req = UpdateThreadRequest::default();\napi.update_thread(id, req).await?; // Err: At least one thread field is required\n\n// after\nlet req = UpdateThreadRequest { title: Some(\"New title\".into()), ..Default::default() };\nif req.has_any_field() {\n    api.update_thread(id, req).await?;\n}","handlingStrategy":"validation","validationCode":"if req.model.is_none() && req.mode.is_none() && req.permission_posture.is_none()\n    && req.title.is_none() && req.system_prompt.is_none() && req.workspace.is_none()\n    && req.allow_shell.is_none() {\n    return; // skip the no-op update entirely\n}","typeGuard":"fn has_any_field(req: &UpdateThreadRequest) -> bool {\n    req.model.is_some() || req.mode.is_some() || req.permission_posture.is_some()\n        || req.title.is_some() || req.system_prompt.is_some() || req.workspace.is_some()\n}","tryCatchPattern":null,"preventionTips":["Build update requests only from actually-changed fields","Treat empty PATCH bodies as a client bug and skip the call","Use get-thread to decide whether an update is needed at all"],"tags":["validation","empty-request","api"],"backgroundTag":"empty-required-field","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}