{"record":{"id":"f0408db47df30842","repo":"jdx/mise","slug":"the-first-agent-request-must-be-hello","errorCode":null,"errorMessage":"the first agent request must be hello","messagePattern":"the first agent request must be hello","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/mise-cache-core/src/agent.rs","lineNumber":1767,"sourceCode":"                    },\n                )\n                .await?;\n                return Ok(());\n            }\n            AgentRequest::Hello { client_version, .. } => {\n                send_response(\n                    &mut writer,\n                    &AgentResponse::Error {\n                        message: format!(\n                            \"cache client {client_version} does not match agent {}\",\n                            self.version\n                        ),\n                    },\n                )\n                .await?;\n                return Ok(());\n            }\n            _ => bail!(\"the first agent request must be hello\"),\n        }\n        send_response(\n            &mut writer,\n            &AgentResponse::Hello {\n                protocol: AGENT_PROTOCOL_VERSION,\n                agent_version: self.version.to_string(),\n            },\n        )\n        .await?;\n\n        while let Some(line) = lines.next_line().await? {\n            let response = match serde_json::from_str(&line) {\n                Ok(request) => self.respond(request).await,\n                Err(error) => AgentResponse::Error {\n                    message: format!(\"invalid agent request: {error}\"),\n                },\n            };\n            send_response(&mut writer, &response).await?;","sourceCodeStart":1749,"sourceCodeEnd":1785,"githubUrl":"https://github.com/jdx/mise/blob/6f52dcdf99e282ef7a7db68c81301fa4618d0f79/crates/mise-cache-core/src/agent.rs#L1749-L1785","documentation":"handle_connection implements a line-delimited request protocol with a mandatory handshake: the first request on a fresh stream must be Hello (a version mismatch gets a dedicated \"cache client X does not match agent Y\" Error response first). Any other first message — Lookup, Record, garbage — bails with this error and the connection ends.","triggerScenarios":"A client sending requests before Hello; a raw probe (nc/curl) writing a non-Hello line; a client whose serde enum encoding deserializes into the wrong variant; a client skipping handshake by bug.","commonSituations":"Hand-rolled or out-of-date clients; health checks poking the socket with arbitrary bytes; protocol version skew between client and agent builds.","solutions":["Send the Hello request as the very first line, matching AgentRequest's serde shape with a client_version field","Read the Hello response: a version-mismatch Error names both client and agent versions — align them","Upgrade the client to a build whose protocol matches the agent"],"exampleFix":"// before\nwriteln!(writer, \"{}\", serde_json::to_string(&AgentRequest::Lookup { .. })?)?;\n// after\nwriteln!(writer, \"{}\", serde_json::to_string(&AgentRequest::Hello { client_version })?)?;\nlet _hello = read_response(&mut reader).await?;\nwriteln!(writer, \"{}\", serde_json::to_string(&AgentRequest::Lookup { .. })?)?;","handlingStrategy":"validation","validationCode":"// Always open the session with Hello and consume the reply before any request\nlet hello = serde_json::to_string(&AgentRequest::Hello { client_version })?;\nwriteln!(writer, \"{hello}\")?; writer.flush().await?;\nmatch read_response(&mut reader).await? {\n    AgentResponse::Hello { agent_version, .. } => { /* check version compatibility */ }\n    AgentResponse::Error { message } => bail!(\"handshake rejected: {message}\"),\n    _ => bail!(\"unexpected first response\"),\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Send Hello as the literal first line on every new connection — no exceptions","Handle the version-mismatch Error response explicitly; it tells you both versions","Keep point health checks from sending arbitrary bytes to the protocol port"],"tags":["mise-cache","protocol","handshake","client-error"],"backgroundTag":"protocol-handshake-failed","analyzedSha":"6f52dcdf99e282ef7a7db68c81301fa4618d0f79","analyzedAt":"2026-08-22T10:14:23.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}