{"record":{"id":"aeebeb339bebd343","repo":"Hmbown/CodeWhale","slug":"the-sandbox-id-is-not-a-usable-path-token","errorCode":null,"errorMessage":"the sandbox id is not a usable path token","messagePattern":"the sandbox id is not a usable path token","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/cloud_dispatch.rs","lineNumber":1421,"sourceCode":"    /// declared `HARNESS_TIMEOUT_SECS`.\n    pub(crate) fn harness_client_budget_secs(command: &HarnessCommand) -> u64 {\n        u64::from(command.timeout_secs).saturating_add(Self::HARNESS_CLIENT_SLACK_SECS)\n    }\n\n    fn api_key() -> Result<String> {\n        read_api_key().ok_or_else(|| anyhow!(missing_credentials_message()))\n    }\n\n    /// Control-plane URL under the validated base.\n    fn control_plane_url(path: &str) -> Result<reqwest::Url> {\n        let base = validate_outbound_origin(&daytona_api_url())?;\n        join_api_path(base, path).context(\"failed to build the cloud agent request URL\")\n    }\n\n    /// Toolbox base for one sandbox: `{toolboxProxyUrl}/{sandboxId}`.\n    fn toolbox_base(receipt: &SandboxReceipt) -> Result<reqwest::Url> {\n        if !valid_sandbox_id(&receipt.sandbox_id) {\n            bail!(\"the sandbox id is not a usable path token\");\n        }\n        let fallback = format!(\"{}/toolbox\", DEFAULT_DAYTONA_API);\n        let raw = receipt.toolbox_url.as_deref().unwrap_or(&fallback);\n        let base = validate_outbound_origin(raw)?;\n        join_api_path(base, &receipt.sandbox_id).context(\"failed to build the sandbox toolbox URL\")\n    }\n\n    /// Apply the dispatch labels via Daytona's dedicated labels endpoint.\n    fn put_sandbox_labels(sandbox_id: &str, api_key: &str, job: &CloudJob) -> Result<()> {\n        if !valid_sandbox_id(sandbox_id) {\n            bail!(\"the sandbox id is not a usable path token\");\n        }\n        let url = Self::control_plane_url(&format!(\"sandbox/{sandbox_id}/labels\"))?;\n        let body = serde_json::json!({\n            \"labels\": {\n                SANDBOX_JOB_LABEL: job.id,\n                \"codewhale.forge\": job.forge.as_str(),\n                SANDBOX_PRODUCT_LABEL: SANDBOX_PRODUCT_VALUE,","sourceCodeStart":1403,"sourceCodeEnd":1439,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/cloud_dispatch.rs#L1403-L1439","documentation":"Before interpolating a sandbox id into control-plane or toolbox URL paths, valid_sandbox_id requires a non-empty token of at most 128 chars made of URL-path-safe characters. If the provider (or a stale receipt) returned an id that fails this check, toolbox_base refuses to build a URL rather than risk path injection or a malformed request.","triggerScenarios":"Calling toolbox_base (or any path built from SandboxReceipt.sandbox_id) with a receipt whose sandbox_id is empty, longer than 128 chars, or contains characters like '/', '?', '#', or whitespace.","commonSituations":"A provider API change altering id format; a stale/corrupted receipt loaded from state; a mock or fake sandbox id used in tests carrying path-special characters.","solutions":["Inspect receipt.sandbox_id — re-create the sandbox so a fresh, provider-issued id replaces the stale one.","Trim/sanitize the id at the boundary where receipts are produced, or reject it earlier with your own check matching valid_sandbox_id's rules (non-empty, <=128 chars, path-safe).","If the provider's ids changed format, update the integration rather than bypassing the check."],"exampleFix":"// before\nlet base = toolbox_base(&receipt)?; // bail on odd id\n// after\nfn is_path_safe(id: &str) -> bool {\n    !id.is_empty() && id.len() <= 128 && id.chars().all(|c| c.is_ascii_alphanumeric() || matches!(c, '-' | '_'))\n}\nanyhow::ensure!(is_path_safe(&receipt.sandbox_id), \"sandbox id invalid: {:?}\", receipt.sandbox_id);\nlet base = toolbox_base(&receipt)?;","handlingStrategy":"validation","validationCode":"fn is_path_safe(id: &str) -> bool {\n    !id.is_empty() && id.len() <= 128 && id.chars().all(|c| c.is_ascii_alphanumeric() || matches!(c, '-' | '_'))\n}\n// before calling any path-building API:\nanyhow::ensure!(is_path_safe(&receipt.sandbox_id), \"bad sandbox id\");","typeGuard":"fn usable_sandbox_id(id: &str) -> Option<&str> {\n    if !id.is_empty() && id.len() <= 128 && id.chars().all(|c| c.is_ascii_alphanumeric() || matches!(c, '-' | '_')) { Some(id) } else { None }\n}","tryCatchPattern":"match toolbox_base(&receipt) {\n    Err(e) if e.to_string().contains(\"not a usable path token\") => eprintln!(\"stale or malformed sandbox id: {:?}\", receipt.sandbox_id),\n    other => other?,\n}","preventionTips":["Validate sandbox ids at receipt creation, not at URL build time.","Refresh stale receipts from the provider before reuse.","Mirror valid_sandbox_id's rules wherever ids cross a trust boundary."],"tags":["validation","path-injection","sandbox","url"],"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-23T08:17:48.524Z"}