{"record":{"id":"727ead9b89726ba1","repo":"astrid-runtime/astrid","slug":"expected-mcp-attach-registration","errorCode":null,"errorMessage":"expected MCP attach registration","messagePattern":"expected MCP attach registration","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-cli/src/commands/mcp/gateway.rs","lineNumber":867,"sourceCode":"    result.map(|_| ())\n}\n\nasync fn read_registration<R>(reader: &mut BufReader<R>) -> Result<AttachRegistration>\nwhere\n    R: AsyncRead + Unpin,\n{\n    timeout(REGISTRATION_TIMEOUT, read_registration_inner(reader))\n        .await\n        .context(\"timed out reading MCP attach registration\")?\n}\n\nasync fn read_registration_inner<R>(reader: &mut BufReader<R>) -> Result<AttachRegistration>\nwhere\n    R: AsyncRead + Unpin,\n{\n    match read_gateway_request_inner(reader).await? {\n        GatewayRequest::Attach(registration) => Ok(registration),\n        GatewayRequest::Control(_) => anyhow::bail!(\"expected MCP attach registration\"),\n    }\n}\n\nasync fn read_gateway_request<R>(reader: &mut BufReader<R>) -> Result<GatewayRequest>\nwhere\n    R: AsyncRead + Unpin,\n{\n    timeout(REGISTRATION_TIMEOUT, read_gateway_request_inner(reader))\n        .await\n        .context(\"timed out reading MCP gateway registration\")?\n}\n\nasync fn read_gateway_request_inner<R>(reader: &mut BufReader<R>) -> Result<GatewayRequest>\nwhere\n    R: AsyncRead + Unpin,\n{\n    let mut line = Vec::new();\n    let mut terminated = false;","sourceCodeStart":849,"sourceCodeEnd":885,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-cli/src/commands/mcp/gateway.rs#L849-L885","documentation":"read_registration_inner reads one line-delimited JSON gateway request from an attach client and requires it to be a GatewayRequest::Attach registration. If the client sends a Control request instead (or any non-Attach variant), the gateway rejects it because the very first preface on an attach connection must be a registration.","triggerScenarios":"An attach client sends a GatewayRequest::Control message as its first preface over the Unix socket; read_registration (used at connection setup and by oversized_registration_preface_fails_without_waiting_for_a_newline) encounters the Control variant.","commonSituations":"A client implementation or script speaks the control protocol (stop/status) over the attach socket instead of the dedicated control path; protocol mix-ups where a reconnect sends the wrong preface; a corrupted or misrouted connection.","solutions":["Ensure the attach client sends a JSON GatewayRequest::Attach registration as the first line after connecting to the attach socket.","Send control operations (stop/status) over the gateway control channel, not the per-attach registration preface.","Check the client's ATTACH_REGISTRATION_VERSION and message shape match the current protocol version.","If writing a custom client, model it on the existing attach implementation: single-line JSON, correct variant, within 16 KiB and REGISTRATION_TIMEOUT."],"exampleFix":"// before: control preface on attach socket\n{\"type\":\"control\",\"version\":1,...}\n// after: attach registration preface\n{\"type\":\"attach\",\"version\":1,\"host\":\"...\",\"host_session_id\":\"...\",\"principal\":\"...\",\"hook_token\":\"...\"}","handlingStrategy":"validation","validationCode":"fn is_attach_preface(first_line: &[u8]) -> bool {\n    serde_json::from_slice::<serde_json::Value>(first_line)\n        .ok()\n        .and_then(|v| v.get(\"type\").cloned())\n        .map_or(false, |t| t == \"attach\")\n}","typeGuard":"fn is_attach(req: &GatewayRequest) -> bool {\n    matches!(req, GatewayRequest::Attach(_))\n}","tryCatchPattern":"match result {\n    Err(e) if e.to_string().contains(\"expected MCP attach registration\") => eprintln!(\"attach socket requires an Attach preface first\"),\n    other => other?,\n}","preventionTips":["Always send the Attach registration as the first line on the attach socket.","Use the control channel for control operations, never the attach preface.","Share one protocol-definition module between client and gateway.","Test the first-line payload shape before deploying custom clients."],"tags":["mcp","protocol","gateway","handshake"],"backgroundTag":"unexpected-api-response-shape","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}