{"record":{"id":"3f0438678ed287de","repo":"Hmbown/CodeWhale","slug":"sandbox-escalation-was-validated-while-planning","errorCode":null,"errorMessage":"sandbox escalation was validated while planning","messagePattern":"sandbox escalation was validated while planning","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/core/engine/turn_loop.rs","lineNumber":3117,"sourceCode":"                            outcomes[plan.index] = Some(ToolExecOutcome {\n                                index: plan.index,\n                                id: tool_id,\n                                name: tool_name,\n                                input: tool_input,\n                                started_at,\n                                terminal: ToolExecutionOutcome::from_legacy(result),\n                                content_blocks: Vec::new(),\n                            });\n                            continue;\n                        }\n\n                        // Handle approval flow: returns (result_override, context_override, approval_stamp)\n                        let model_requested_policy = requested_sandbox_escalation(\n                            &tool_name,\n                            &tool_input,\n                            &batch_sandbox_policy,\n                        )\n                        .expect(\"sandbox escalation was validated while planning\")\n                        .map(|(policy, _)| policy);\n                        let (result_override, context_override, approval_stamp): (\n                            Option<Result<ToolResult, ToolError>>,\n                            Option<crate::tools::ToolContext>,\n                            Option<ToolApprovalStamp>,\n                        ) = if plan.approval_required {\n                            emit_tool_audit(json!({\n                                \"event\": \"tool.approval_required\",\n                                \"tool_id\": tool_id.clone(),\n                                \"tool_name\": tool_name.clone(),\n                            }));\n                            let approval_key = crate::tools::approval_cache::build_approval_key(\n                                &tool_name,\n                                &tool_input,\n                            )\n                            .0;\n                            let approval_grouping_key =\n                                crate::tools::approval_cache::build_approval_grouping_key(","sourceCodeStart":3099,"sourceCodeEnd":3135,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/core/engine/turn_loop.rs#L3099-L3135","documentation":"Panic when the execution phase re-runs `requested_sandbox_escalation` for a tool call and it returns `Err`. The expect encodes the assumption that the planning phase already validated the identical call (invalid calls were converted into an error result and `continue`d before this point). The validator returns `Err` for concrete input failures: `sandbox_permissions` on a bash action other than `run`, a missing or whitespace-only `justification`, a non-string permission, or an unrecognized permission level for the effective policy.","triggerScenarios":"State drift between the planning pass and the execution pass: `tool_input` rewritten between phases (e.g. by the approval flow), a different `batch_sandbox_policy` in effect at execution time, or a new tool-call routing path that reaches execution without going through plan-time validation. Concretely, a call like `{\"action\":\"preview\",\"sandbox_permissions\":\"danger-full-access\"}` that planning tolerated but execution rejects.","commonSituations":"Adding input mutation between planning and execution; changing how the batch sandbox policy is computed mid-turn (Runtime posture switch); wiring new tool-call sources (fleet workers, subagents) into execution but not into planning validation.","solutions":["Replace the expect with a `match` that turns `Err` into a model-visible tool error outcome and `continue`s, mirroring the planning path's handling.","Ensure any mutation of `tool_input` between planning and execution re-runs the escalation validation.","Freeze `(tool_name, tool_input, batch_sandbox_policy)` for the whole plan-execute lifecycle, or version them so drift is detectable.","Add a test that flips the effective sandbox posture between planning and execution and asserts a graceful error."],"exampleFix":"// before\nlet model_requested_policy = requested_sandbox_escalation(\n    &tool_name, &tool_input, &batch_sandbox_policy,\n).expect(\"sandbox escalation was validated while planning\").map(|(policy, _)| policy);\n\n// after: re-validation failure becomes a model-visible tool error\nlet model_requested_policy = match requested_sandbox_escalation(\n    &tool_name, &tool_input, &batch_sandbox_policy,\n) {\n    Ok(opt) => opt.map(|(policy, _)| policy),\n    Err(err) => {\n        outcomes[plan.index] = Some(ToolExecOutcome::error(plan.index, err.to_string()));\n        continue;\n    }\n};","handlingStrategy":"validation","validationCode":"// Re-run the identical validation before dispatch; route Err to a model-visible error\nif let Err(err) = requested_sandbox_escalation(&tool_name, &tool_input, &batch_sandbox_policy) {\n    return model_visible_tool_error(tool_id, err.to_string());\n}","typeGuard":"fn escalation_is_reproducible(\n    tool_name: &str,\n    input: &serde_json::Value,\n    policy: &crate::sandbox::SandboxPolicy,\n) -> bool {\n    requested_sandbox_escalation(tool_name, input, policy).is_ok()\n}","tryCatchPattern":null,"preventionTips":["Freeze `(tool_name, tool_input, batch_sandbox_policy)` between planning and execution.","Route every new tool-call source through plan-time validation before it can reach execution."],"tags":["rust","sandbox","validation","plan-execute-drift","panic","expect"],"backgroundTag":"invalid-tool-input","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}