xai-org/grok-build · error

-32601

-32601

Error message

Method not found: {other}

What it means

The headless ext-method responder (reply_headless_ext_method) answers known interaction methods (x.ai/ask_user_question, x.ai/exit_plan_mode, x.ai/mcp/elicit) with policy replies; any other method gets this JSON-RPC -32601 Method not found error on the response channel. This keeps headless runs from failing the whole turn when the UI is absent.

Solutions

  1. Confirm the method name matches one supported headless (ask_user_question, exit_plan_mode, mcp/elicit)
  2. Update reply_headless_ext_method with a policy reply for the new method if you own the code
  3. Run the client in interactive mode when the method requires a UI
  4. Check client/server version alignment

Example fix

// before
other => Err(acp::Error::new(-32601, format!("Method not found: {other}")))
// after
"x.ai/mcp/elicit" => ext_response_from(&McpElicitExtResponse::default()),
other => Err(acp::Error::new(-32601, format!("Method not found: {other}")))
Defensive patterns

Strategy: try-catch

Validate before calling

const HEADLESS_SUPPORTED = ["x.ai/ask_user_question", "x.ai/exit_plan_mode", "x.ai/mcp/elicit"];
if (!HEADLESS_SUPPORTED.includes(method)) console.warn(`${method} has no headless policy`);

Try / catch

match response {
  Err(e) if e.code == -32601 => eprintln!("headless mode does not support: {}", e.message),
  Ok(resp) => use(resp),
}

Prevention

When it happens

Trigger: A reverse ext_method request arrives during a headless run with a method string not in the headless responder's match list — typos, version drift, or a new interactive-only method not yet given a headless policy.

Common situations: Server emitting a newly added x.ai/* ext method while the headless protocol layer hasn't been updated; client invoking interactive methods in headless mode.

Related errors


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

Appendix: source

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

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.".
        "x.ai/exit_plan_mode" => ext_response_from(&ExitPlanModeExtResponse {
            outcome: "approved".to_string(),
            feedback: None,
        }),
        other => Err(acp::Error::new(
            -32601,
            format!("Method not found: {other}"),
        )),
    };
    args.response_tx.send(response).ok();
}

/// Coerce a numeric `task_id` (version skew) to a string so it does not fail the decode and leak an untracked background task.
fn de_task_id<'de, D>(deserializer: D) -> Result<String, D::Error>
where
    D: serde::Deserializer<'de>,
{
    use serde::Deserialize;
    match serde_json::Value::deserialize(deserializer)? {
        serde_json::Value::String(s) => Ok(s),
        serde_json::Value::Number(n) => Ok(n.to_string()),
        other => Err(serde::de::Error::custom(format!(
            "task_id must be a JSON string or number, got {other}"

View on GitHub (pinned to bc7f02eddd)