Hmbown/CodeWhale · error

registered shell tool context

Error message

registered shell tool context

What it means

Panic while applying a user's approval to a sandbox escalation. `batch_tool_context` comes from `self.live_tool_context(tool_registry)` (engine.rs:5467), which returns `None` only when no tool registry exists (`registry?` at the top of the function). When the approval decision is `Approved` and the model requested a policy, the code clones that context to call `with_elevated_sandbox_policy`. The expect assumes that a bash escalation path implies a registry-backed live context; it fires when escalation approval is reached with the registry absent.

Source

Thrown at crates/tui/src/core/engine/turn_loop.rs:3177

                                Ok(ApprovalResult::Approved) => {
                                    let decision = if model_requested_policy.is_some() {
                                        "approved_with_requested_policy"
                                    } else {
                                        "approved"
                                    };
                                    emit_tool_audit(json!({
                                        "event": "tool.approval_decision",
                                        "tool_id": tool_id.clone(),
                                        "tool_name": tool_name.clone(),
                                        "decision": decision,
                                        "policy": model_requested_policy.as_ref().map(|policy| format!("{policy:?}")),
                                        "caller": caller_type_for_tool_use(tool_caller.as_ref()),
                                    }));
                                    if let Some(policy) = model_requested_policy {
                                        let elevated_context = Some(
                                            batch_tool_context
                                                .clone()
                                                .expect("registered shell tool context")
                                                .with_elevated_sandbox_policy(policy),
                                        );
                                        (
                                            None,
                                            elevated_context,
                                            Some(ToolApprovalStamp::ApprovedWithPolicy),
                                        )
                                    } else {
                                        (None, None, Some(ToolApprovalStamp::ApprovedByUser))
                                    }
                                }
                                Ok(ApprovalResult::Denied) => {
                                    emit_tool_audit(json!({
                                        "event": "tool.approval_decision",
                                        "tool_id": tool_id.clone(),
                                        "tool_name": tool_name.clone(),
                                        "decision": "denied",
                                        "caller": caller_type_for_tool_use(tool_caller.as_ref()),

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Replace the expect with `batch_tool_context.as_ref().map(|ctx| ctx.clone().with_elevated_sandbox_policy(policy))` and treat `None` as fail-closed: emit a denial result stating the elevated context is unavailable.
  2. Assert the registry invariant at batch entry and route escalation-capable calls away when it is missing.
  3. Document that `live_tool_context` may return `None` only for registry absence, and keep that contract.
  4. Add a test that runs the batch path with no registry and a `sandbox_permissions`-bearing call.

Example fix

// before
let elevated_context = Some(
    batch_tool_context.clone().expect("registered shell tool context")
        .with_elevated_sandbox_policy(policy),
);

// after: fail closed when no live context exists
let elevated_context = batch_tool_context
    .as_ref()
    .map(|ctx| ctx.clone().with_elevated_sandbox_policy(policy));
Defensive patterns

Strategy: type-guard

Validate before calling

if tool_input.get("sandbox_permissions").is_some() && batch_tool_context.is_none() {
    // fail closed before the approval prompt is ever shown
    return tool_error("sandbox escalation requires a live tool context");
}

Type guard

fn can_apply_escalation(ctx: &Option<ToolContext>, requested: bool) -> bool {
    !requested || ctx.is_some()
}

Prevention

When it happens

Trigger: A batch execution path running with `tool_registry = None` while a tool call carries `sandbox_permissions` and the user approves it: the registry is lost or never passed on a new execution entry point, or `live_tool_context` is refactored to return `None` in more cases while escalation handling still assumes `Some`.

Common situations: Embedding the engine in tests or fleet workers without a full tool registry; refactors that make the registry optional; split-brain between escalation planning (which does not need the registry) and approval application (which does).

Related errors


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/a077782ca5a7ad43. Report an issue: GitHub.