{"record":{"id":"c5a64ed9474a9a36","repo":"Hmbown/CodeWhale","slug":"validated-sandbox-permission","errorCode":null,"errorMessage":"validated sandbox permission","messagePattern":"validated sandbox permission","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/core/engine/turn_loop.rs","lineNumber":2543,"sourceCode":"\n                // Bind escalation last so remembered rules cannot remove its\n                // prompt and later safety/repo-law holds cannot hide what the\n                // elevated approval grants. A hard block above still wins.\n                if blocked_error.is_none() {\n                    match requested_sandbox_escalation(\n                        &tool_name,\n                        &tool_input,\n                        &batch_sandbox_policy,\n                    ) {\n                        Ok(Some((_policy, justification)))\n                            if batch_approval_mode\n                                == crate::tui::approval::ApprovalMode::Suggest =>\n                        {\n                            let escalation_description = format!(\n                                \"Sandbox escalation to '{}' for this exact call: {justification}\",\n                                tool_input[\"sandbox_permissions\"]\n                                    .as_str()\n                                    .expect(\"validated sandbox permission\")\n                            );\n                            approval_description = if approval_force_prompt {\n                                format!(\n                                    \"{escalation_description}. Additional approval gate: {approval_description}\"\n                                )\n                            } else {\n                                escalation_description\n                            };\n                            approval_required = true;\n                            approval_force_prompt = true;\n                        }\n                        Ok(Some(_)) => {\n                            blocked_error = Some(ToolError::permission_denied(format!(\n                                \"Sandbox escalation requires a one-shot user approval, but the current {} posture cannot provide it. Switch to Ask or continue without escalation.\",\n                                batch_approval_mode.permission_chip_label()\n                            )));\n                        }\n                        Ok(None) => {}","sourceCodeStart":2525,"sourceCodeEnd":2561,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/core/engine/turn_loop.rs#L2525-L2561","documentation":"Panic while formatting the sandbox escalation description during approval planning. After `requested_sandbox_escalation` (turn_loop.rs:158) returns `Ok(Some((policy, justification)))`, the code re-reads `tool_input[\"sandbox_permissions\"]` and calls `.as_str().expect(...)`. That validator already rejects non-string permissions (`\"sandbox_permissions must be a string\"`), so the expect assumes the plan-time validation proved string-ness of the very same value; it fires only when the value re-read here is not a JSON string, i.e. validation and the description builder are looking at different data.","triggerScenarios":"A bash/exec_shell call carrying `sandbox_permissions` for which planning-time validation returned Ok(Some(...)) but whose `tool_input[\"sandbox_permissions\"]` is not a string at description time: the input was mutated between validation and formatting, or the validator was refactored to stop enforcing `as_str` while this call site kept the expect.","commonSituations":"Refactoring the escalation validator to accept structured permissions (arrays/objects); inserting an input-rewriting step between plan validation and approval description formatting; duplicated validation logic drifting apart between phases.","solutions":["Return the validated permission string from `requested_sandbox_escalation` (add it to the `Ok(Some(...))` tuple) and build the description from that string instead of re-reading `tool_input`.","If re-reading stays, replace `.expect` with `.and_then(serde_json::Value::as_str).unwrap_or(\"workspace-write\")` so a mismatch degrades to a readable description instead of a panic.","Add a unit test that pushes a non-string `sandbox_permissions` through the planning path.","Grep the approval block for other re-reads of validated fields (e.g. `justification`) and route them through the validator output too."],"exampleFix":"// before\nlet escalation_description = format!(\n    \"Sandbox escalation to '{}' for this exact call: {justification}\",\n    tool_input[\"sandbox_permissions\"].as_str().expect(\"validated sandbox permission\")\n);\n\n// after: consume the string the validator already checked\nOk(Some((_policy, justification, requested.to_string()))) => {\n    let escalation_description = format!(\n        \"Sandbox escalation to '{requested}' for this exact call: {justification}\"\n    );","handlingStrategy":"validation","validationCode":"fn escalation_input_shape_ok(input: &serde_json::Value) -> bool {\n    input.get(\"sandbox_permissions\")\n        .map_or(true, serde_json::Value::is_string)\n        && input.get(\"justification\")\n            .map_or(true, serde_json::Value::is_string)\n}","typeGuard":"fn bash_run_with_escalation(input: &serde_json::Value) -> bool {\n    input.get(\"action\")\n        .and_then(|v| v.as_str())\n        .map_or(true, |a| a == \"run\")\n        && input.get(\"sandbox_permissions\").is_some()\n        && input.get(\"justification\")\n            .and_then(|v| v.as_str())\n            .map_or(false, |j| !j.trim().is_empty())\n}","tryCatchPattern":"let desc = std::panic::catch_unwind(|| build_escalation_description(&tool_input, &justification));\nlet desc = desc.unwrap_or_else(|_| \"sandbox escalation (permission unreadable)\".to_string());","preventionTips":["Consume the validator's output instead of re-indexing raw tool input.","Change the accepted shape of `sandbox_permissions` only together with `requested_sandbox_escalation` and the description builder."],"tags":["rust","sandbox","approval","json-type","panic","expect"],"backgroundTag":"json-field-type-mismatch","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}