astrid-runtime/astrid · error
MCP gateway control authority is incomplete
Error message
MCP gateway control authority is incomplete
What it means
A parsed GatewayRequest::Control must carry authority proof: a nonzero pid and a non-empty hook_token. read_gateway_request_inner rejects control messages missing either. This ensures only a process that legitimately holds the gateway's minted hook capability can issue stop/status operations.
Source
Thrown at crates/astrid-cli/src/commands/mcp/gateway.rs:911
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
);
}
super::lifecycle::resolve_principal(Some(®istration.principal))?;
if registration.host.trim().is_empty() {
anyhow::bail!("MCP attach registration has an empty host");
}View on GitHub (pinned to affd8760f4)
Solutions
- Populate hook_token with the token minted at gateway startup (mint_hook_token) before sending any control request.
- Set pid to the real OS process id (e.g. std::process::id()) — pid 0 is rejected.
- If the token is unknown, restart the gateway and read the fresh token from its ready/lease files rather than guessing.
- Validate the control struct before sending: non-zero pid, non-blank token.
Example fix
// before
let ctl = GatewayControlRequest { version: GATEWAY_CONTROL_VERSION, ..Default::default() };
// after
let ctl = GatewayControlRequest { version: GATEWAY_CONTROL_VERSION, pid: std::process::id(), hook_token: hook_token.clone(), .. };
Defensive patterns
Strategy: validation
Validate before calling
fn control_authority_ok(pid: u32, token: &str) -> bool {
pid != 0 && !token.trim().is_empty()
} Type guard
fn has_authority(ctl: &GatewayControlRequest) -> bool {
ctl.pid != 0 && !ctl.hook_token.trim().is_empty()
} Try / catch
match result {
Err(e) if e.to_string().contains("control authority is incomplete") => eprintln!("control request needs nonzero pid and the minted hook_token"),
other => other?,
} Prevention
- Always load the hook token minted at gateway startup before issuing control requests.
- Avoid Default::default() on control structs; set pid and token explicitly.
- Never send pid = 0.
- Refuse to send control messages when token acquisition failed upstream.
When it happens
Trigger: Sending a control request where control.pid == 0 or control.hook_token.trim() is empty/whitespace, parsed in read_gateway_request_inner.
Common situations: Hand-crafted control payloads omitting the token; a client whose token-minting step failed or was skipped; serializing a GatewayControlRequest with default/uninitialized fields (pid defaults to 0, empty token).
Related errors
- MCP attach registration has an empty host
- MCP attach registration is missing hook_token
- MCP attach host session key is empty
- MCP attach host is empty
- MCP gateway attach limit reached for principal '{principal}'
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/9cbbe18cf765bbf7.
Report an issue: GitHub.