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.

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)

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.