{"record":{"id":"a3417c001c8b65d2","repo":"openai/codex","slug":"app-server-user-agent-omitted-version","errorCode":null,"errorMessage":"app-server user-agent omitted version","messagePattern":"app-server user-agent omitted version","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"codex-rs/app-server-daemon/src/client.rs","lineNumber":156,"sourceCode":"            .await\n            .ok_or_else(|| anyhow!(\"app-server closed the control socket\"))??;\n        let Message::Text(payload) = frame else {\n            continue;\n        };\n        return serde_json::from_str::<JSONRPCMessage>(&payload)\n            .context(\"failed to parse app-server JSON-RPC message\");\n    }\n}\n\nfn parse_version_from_user_agent(user_agent: &str) -> Result<String> {\n    let (_originator, rest) = user_agent\n        .split_once('/')\n        .ok_or_else(|| anyhow!(\"app-server user-agent omitted version separator\"))?;\n    let version = rest\n        .split_whitespace()\n        .next()\n        .filter(|version| !version.is_empty())\n        .ok_or_else(|| anyhow!(\"app-server user-agent omitted version\"))?;\n    Ok(version.to_string())\n}\n\n#[cfg(all(test, unix))]\nmod tests {\n    use pretty_assertions::assert_eq;\n\n    use super::parse_version_from_user_agent;\n\n    #[test]\n    fn parses_version_from_codex_user_agent() {\n        assert_eq!(\n            parse_version_from_user_agent(\n                \"codex_app_server_daemon/1.2.3 (Linux 6.8.0; x86_64) codex_cli_rs/1.2.3\",\n            )\n            .expect(\"version\"),\n            \"1.2.3\"\n        );","sourceCodeStart":138,"sourceCodeEnd":174,"githubUrl":"https://github.com/openai/codex/blob/339751715c64496cb86246bfb3935f40e309dd3d/codex-rs/app-server-daemon/src/client.rs#L138-L174","documentation":"The second half of the version parse: after splitting on '/', the remainder must start with a non-empty whitespace-delimited token (the version). A user_agent like 'codex_app_server/' or one whose post-slash text is only whitespace fails here. Like [47] it fires inside probe() from the InitializeResponse, signaling a malformed or foreign agent string rather than a transport problem.","triggerScenarios":"probe() against a server whose user_agent ends at the separator ('name/') or continues with only whitespace or parenthetical junk before any version token; custom builds composing the agent from parts and emitting an empty version.","commonSituations":"Hand-built app-server releases that template the version in separately and ship an empty token; forks renaming the version field; test stubs returning placeholder agent strings.","solutions":["Fix the server's user_agent to 'name/x.y.z …' with a real version token first.","Restart the daemon to respawn a managed server with a known-good agent string.","Verify daemon and app-server come from the same codex release.","If the version is genuinely unavailable, decide explicitly how to degrade instead of letting probe abort the lifecycle."],"exampleFix":"// before\nuser_agent: format!(\"{}/\", name),\n// after\nuser_agent: format!(\"{}/{}\", name, env!(\"CARGO_PKG_VERSION\")),","handlingStrategy":"fallback","validationCode":null,"typeGuard":"fn user_agent_has_version(user_agent: &str) -> bool {\n    user_agent\n        .split_once('/')\n        .and_then(|(_, rest)| rest.split_whitespace().next())\n        .is_some_and(|version| !version.is_empty())\n}","tryCatchPattern":"let info = match probe(&path).await {\n    Ok(info) => info,\n    Err(err) if err.to_string().contains(\"user-agent omitted version\") => {\n        ProbeInfo { app_server_version: \"unknown\".into() } // degrade, then restart the managed server\n    }\n    Err(err) => return Err(err),\n};","preventionTips":["Always compose user_agent as 'name/<real version>', never a bare separator.","Feed the version from the build system (env!(\"CARGO_PKG_VERSION\")) so it can't be empty.","Test custom servers against probe() in CI to catch agent-string regressions."],"tags":["rust","codex","daemon","user-agent","version-mismatch"],"backgroundTag":"user-agent-parsing","analyzedSha":"339751715c64496cb86246bfb3935f40e309dd3d","analyzedAt":"2026-08-25T05:35:09.876Z","schemaVersion":2},"datasetVersion":"2026-08-25T06:17:31.827Z"}