astrid-runtime/astrid · error

unsupported MCP gateway control version {}

Error message

unsupported MCP gateway control version {}

What it means

Control requests on the gateway socket carry a protocol version field that must equal GATEWAY_CONTROL_VERSION. When a client sends a GatewayRequest::Control whose version differs, read_gateway_request_inner rejects it. This guards the control channel against clients speaking an incompatible protocol revision.

Source

Thrown at crates/astrid-cli/src/commands/mcp/gateway.rs:905

        let byte = reader
            .read_u8()
            .await
            .context("failed to read MCP attach registration")?;
        if byte == b'\n' {
            terminated = true;
            break;
        }
        line.push(byte);
    }
    if !terminated {
        anyhow::bail!("MCP attach registration is missing or too large");
    }
    let request: GatewayRequest =
        serde_json::from_slice(&line).context("MCP gateway registration is not valid JSON")?;
    match &request {
        GatewayRequest::Control(control) => {
            if control.version != GATEWAY_CONTROL_VERSION {
                anyhow::bail!(
                    "unsupported MCP gateway control version {}",
                    control.version
                );
            }
            if control.pid == 0 || control.hook_token.trim().is_empty() {
                anyhow::bail!("MCP gateway control authority is incomplete");
            }
        },
        GatewayRequest::Attach(registration) => validate_registration(registration)?,
    }
    Ok(request)
}

fn validate_registration(registration: &AttachRegistration) -> Result<()> {
    if registration.version != ATTACH_REGISTRATION_VERSION {
        anyhow::bail!(
            "unsupported MCP attach registration version {}",
            registration.version

View on GitHub (pinned to affd8760f4)

Solutions

  1. Upgrade or downgrade so the client and gateway come from the same build; check `astrid --version` on both sides.
  2. Restart/kill any stale gateway daemon left from a previous version so a fresh one with matching GATEWAY_CONTROL_VERSION starts.
  3. Update custom control scripts to use the current GATEWAY_CONTROL_VERSION constant from the codebase.
  4. Clear stale socket/lease files under daemon_root before reconnecting so the old-version gateway is not reused.

Example fix

// before: custom script pins an old version
{"type":"control","version":0,"pid":1234,"hook_token":"..."}
// after: use the constant the library exports
let req = json!({"type":"control","version":GATEWAY_CONTROL_VERSION,"pid":std::process::id(),"hook_token":token});
Defensive patterns

Strategy: validation

Validate before calling

fn control_version_ok(v: u32, expected: u32) -> bool { v == expected }

Try / catch

match result {
    Err(e) if e.to_string().contains("unsupported MCP gateway control version") => eprintln!("client/gateway version skew: restart the gateway and match versions"),
    other => other?,
}

Prevention

When it happens

Trigger: Sending a control request (stop/status) with control.version != GATEWAY_CONTROL_VERSION from read_gateway_request/read_registration_inner parsing.

Common situations: Mixed-version CLI and daemon after an upgrade (old astrid binary talking to a newer gateway or vice versa); hand-crafted control scripts with a hardcoded version number; a stale daemon running old code.

Related errors


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/081f7eb51284f006. Report an issue: GitHub.