{"record":{"id":"573ac85b45a8fb62","repo":"Hmbown/CodeWhale","slug":"invalid-durable-task-id","errorCode":null,"errorMessage":"Invalid durable task id","messagePattern":"Invalid durable task id","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/task_manager.rs","lineNumber":1620,"sourceCode":"        self.add_task_with_id(req, Self::new_task_id()).await\n    }\n\n    /// Allocate the durable owner identity before queue insertion so callers\n    /// can register graph spawn intent first.\n    #[must_use]\n    pub(crate) fn new_task_id() -> String {\n        format!(\"task_{}\", &Uuid::new_v4().simple().to_string()[..16])\n    }\n\n    /// Read the exact durable task binding without adopting another process's\n    /// queue. Used by the automation dispatcher while it owns the store claim.\n    pub(crate) fn read_bound_task(&self, task_id: &str) -> Result<Option<TaskRecord>> {\n        if task_id.is_empty()\n            || !task_id\n                .bytes()\n                .all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b'_' | b'-'))\n        {\n            bail!(\"Invalid durable task id\");\n        }\n        read_bound_task_file(&self.tasks_dir.join(format!(\"{task_id}.json\")), task_id)\n    }\n\n    /// Recover a persisted automation admission under its cross-process\n    /// dispatch lock. A promoted task is accepted work, including failed or\n    /// interrupted work; returning it must never enqueue it again.\n    pub(crate) async fn recover_task_admission(\n        &self,\n        request: NewTaskRequest,\n        task_id: String,\n    ) -> Result<TaskRecord> {\n        validate_preallocated_task_id(&task_id)?;\n        if let Some(task) = self.read_bound_task(&task_id)? {\n            validate_bound_task_request(&task, &request)?;\n            return Ok(task);\n        }\n        let staged_path = self.tasks_dir.join(format!(\".{task_id}.json.pending\"));","sourceCodeStart":1602,"sourceCodeEnd":1638,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/task_manager.rs#L1602-L1638","documentation":"read_bound_task validates the durable task id before using it to build a file path: the id must be non-empty and contain only ASCII alphanumerics, '_', or '-'. Anything else (slashes, dots, spaces, unicode) is rejected to prevent path traversal or malformed record lookups against the tasks directory.","triggerScenarios":"Calling read_bound_task with an empty string, an id containing '/' or '..' or '.', a URL-encoded or whitespace-padded id, or an id read from external storage that was serialized differently.","commonSituations":"Persisting task ids in a UI state file that escapes them; passing a filename (\"abc.json\") instead of the bare id; user-typed ids from a CLI argument; older records using dotted ids.","solutions":["Pass the bare task id exactly as produced by the task manager (alphanumeric, '_' or '-' only)","Strip file extensions and path components: use the id, not \"<id>.json\" or a full path","Trim and validate the id in the caller with the same charset rule before invoking read_bound_task","If ids come from external config, regenerate them via the task manager's id generation"],"exampleFix":"// before\nlet record = mgr.read_bound_task(&format!(\"{id}.json\"))?;\n// after\nlet bare = id.trim().trim_end_matches(\".json\");\nif !bare.is_empty() && bare.bytes().all(|b| b.is_ascii_alphanumeric() || matches!(b, b'_' | b'-')) {\n    let record = mgr.read_bound_task(bare)?;\n}","handlingStrategy":"validation","validationCode":"fn valid_task_id(s: &str) -> bool { !s.is_empty() && s.bytes().all(|b| b.is_ascii_alphanumeric() || matches!(b, b'_' | b'-')) }","typeGuard":null,"tryCatchPattern":"if let Err(e) = mgr.read_bound_task(id) { if e.to_string().contains(\"Invalid durable task id\") { /* sanitize id and retry */ } }","preventionTips":["Pass bare ids, never filenames or paths","Trim ids read from external sources before use","Persist ids exactly as generated, without escaping or encoding"],"tags":["rust","validation","identifier","path-safety"],"backgroundTag":"invalid-identifier-format","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"}