xai-org/grok-build · error

-32603

-32603

Error message

serialize ext response: {e}

What it means

A -32603 internal error from ext_response_from when serde_json::value::to_raw_value cannot serialize the typed ext-method reply (e.g. AskUserQuestionExtResponse). It exists so the response oneshot is always answered with an explicit ACP error instead of silently dropping the sender.

Source

Thrown at crates/codegen/xai-grok-pager/src/headless/ext_protocol.rs:14

//! Decodes the shell's `x.ai/*` extension notifications into the headless [`ExtEvent`] the orchestrator dispatches.
//! Also answers reverse `ext_method` requests with policy replies.
//! This module owns the wire envelope shapes and the method-to-event mapping, kept out of `headless.rs`.

use agent_client_protocol as acp;
use xai_acp_lib::{AcpArgsBox, AcpResult};

use crate::headless::reducer::{Lifecycle, StreamEvent};

/// Serialize a typed ext-method reply; a serialize failure becomes an explicit ACP error so the oneshot is always answered.
fn ext_response_from<T: serde::Serialize>(value: &T) -> AcpResult<acp::ExtResponse> {
    serde_json::value::to_raw_value(value)
        .map(|raw| acp::ExtResponse::new(raw.into()))
        .map_err(|e| acp::Error::new(-32603, format!("serialize ext response: {e}")))
}

/// Answer a reverse `ext_method` request without a UI.
/// Known interaction methods get a policy reply; dropping `response_tx` instead would fail the whole turn with a channel `recv_failed`.
pub(crate) fn reply_headless_ext_method(args: AcpArgsBox<acp::ExtRequest>) {
    use xai_grok_tools::implementations::grok_build::ask_user_question::AskUserQuestionExtResponse;
    use xai_grok_tools::implementations::grok_build::exit_plan_mode::ExitPlanModeExtResponse;

    let method = args.request.method.as_ref();
    // Known methods are answered without parsing params: even a malformed request gets the policy reply rather than a dropped channel
    let response = match method {
        // The model sees the tool's NO_OPERATOR_TEXT (headless sessions are non-interactive), not the interactive "user declined" cancel text
        "x.ai/ask_user_question" => ext_response_from(&AskUserQuestionExtResponse::Cancelled),
        "x.ai/mcp/elicit" => {
            use xai_grok_tools::mcp_elicitation::McpElicitExtResponse;
            ext_response_from(&McpElicitExtResponse::Cancel)
        }
        // The model sees "Your plan has been approved. You can now start coding.".

View on GitHub (pinned to bc7f02eddd)

Solutions

  1. Inspect '{e}' for the serde detail naming the unserializable field
  2. Fix the response struct (use string map keys, serializable field types)
  3. Validate the reply type with a round-trip serialize test
  4. Keep ext response payloads plain JSON-compatible

Example fix

// before
struct Reply { meta: std::collections::HashMap<i32, String> }  // non-string keys
// after
struct Reply { meta: std::collections::HashMap<String, String> }
Defensive patterns

Strategy: try-catch

Validate before calling

// round-trip check the reply type in tests
let raw = serde_json::to_value(&reply).expect("ext reply must be JSON-serializable");
assert!(raw.is_object());

Try / catch

match ext_response_from(&reply) {
  Err(e) if e.code == -32603 => eprintln!("serialize failed: {}", e.message),
  Ok(resp) => send(resp),
}

Prevention

When it happens

Trigger: Serializing an ExtResponse payload that serde cannot convert to RawValue — practically only for non-string-map keys (e.g. HashMap with non-string keys), NaN/f64 in strict positions, or a poisoned serializer for exotic types in the response struct.

Common situations: Custom response types added to the headless ext protocol containing maps with non-string keys or non-serializable fields; schema changes introducing such fields.

Related errors


AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31). Data as JSON: /api/errors/e81c7ee466a557eb. Report an issue: GitHub.