t8y2/dbx · error

agent request panic: %v

Error message

agent request panic: %v

What it means

The agent process wraps each request handler in a recover() guard so a panic anywhere on the request path is converted into a structured error response ("agent request panic: %v") instead of crashing the process and breaking the JSON-RPC-over-stdout stream. The variable resp (the function's named return) carries this error back to the client, which sees it as the response's error field.

Source

Thrown at agents/drivers/oracle-go/main.go:646

	}
	requests.Wait()
	if err := scanner.Err(); err != nil && !errors.Is(err, io.EOF) {
		fmt.Fprintf(os.Stderr, "failed to read stdin: %v\n", err)
	}
}

func newRuntimeServer() *runtimeServer {
	return &runtimeServer{sessions: map[string]*agentSession{}}
}

func (r *runtimeServer) handleLine(line string) (resp response, shutdown bool) {
	var req request
	// Last-resort guard: a panic anywhere on the request path must not kill
	// the agent process and break the RPC stream, so convert it to a readable
	// error response instead of an end-of-stream failure.
	defer func() {
		if recovered := recover(); recovered != nil {
			resp = errorResponse(req.ID, fmt.Errorf("agent request panic: %v", recovered))
			shutdown = false
		}
	}()
	if err := json.Unmarshal([]byte(line), &req); err != nil {
		return errorResponse(nil, err), false
	}
	if len(req.ID) == 0 {
		req.ID = json.RawMessage("1")
	}
	result, shouldShutdown, err := r.dispatch(req.Method, req.Params)
	if err != nil {
		return errorResponse(req.ID, err), false
	}
	return response{JSONRPC: "2.0", ID: req.ID, Result: result}, shouldShutdown
}

func (r *runtimeServer) dispatch(method string, params map[string]json.RawMessage) (any, bool, error) {
	switch method {

View on GitHub (pinned to c0390bff16)

Solutions

  1. Read the %v detail in the response error to identify the panicking value/operation
  2. Fix the handler bug revealed by the panic (nil checks, safe type assertions, bounds checks)
  3. Validate/sanitize request params on the client before sending so handlers receive expected shapes
  4. Update the agent binary if the panic is a known fixed bug
  5. Report the stack (add debug logging around the recover) to the driver maintainers if reproducible

Example fix

// handler before
limit := params["limit"].(int) // panics if float64 or missing
// after
limit, ok := params["limit"].(float64)
if !ok { return errorResponse(req.ID, fmt.Errorf("invalid limit")) }
Defensive patterns

Strategy: try-catch

Validate before calling

# client-side: shape-check params before sending to avoid handler panics
required = {"sql": str, "params": (dict, type(None))}
for key, types in required.items():
    if key in payload and not isinstance(payload[key], types):
        raise TypeError(f"param {key!r} must be {types}")

Type guard

def is_valid_response(resp: dict) -> bool:
    return isinstance(resp, dict) and ("error" in resp or "result" in resp) and not ("error" in resp and "result" in resp)

Try / catch

try:
    response = send_request(line)
except AgentRPCError as e:
    if "agent request panic" in str(e):
        log.error("agent panicked on request: %s", e)
        # retry once with sanitized params or fail fast; do not reconnect blindly
        raise
    raise

Prevention

When it happens

Trigger: Any runtime panic while handling a request line: nil map/slice access, type assertion failure on unmarshalled params, index out of range, or a bug in a method handler. The deferred recover catches it, sets resp to an errorResponse, and prevents shutdown.

Common situations: Client sends params of unexpected shape (missing field, wrong type) that a handler asserts; query results larger than expected triggering an index bug; a driver/runtime internal error surfacing as a panic under concurrency.

Related errors


AI-assisted analysis of t8y2/dbx@c0390bff16 (2026-09-05). Data as JSON: /api/errors/e507ca0a86b0713a. Report an issue: GitHub.