openai/codex · error · anyhow::Error

app-server user-agent omitted version

Error message

app-server user-agent omitted version

What it means

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.

Source

Thrown at codex-rs/app-server-daemon/src/client.rs:156

            .await
            .ok_or_else(|| anyhow!("app-server closed the control socket"))??;
        let Message::Text(payload) = frame else {
            continue;
        };
        return serde_json::from_str::<JSONRPCMessage>(&payload)
            .context("failed to parse app-server JSON-RPC message");
    }
}

fn parse_version_from_user_agent(user_agent: &str) -> Result<String> {
    let (_originator, rest) = user_agent
        .split_once('/')
        .ok_or_else(|| anyhow!("app-server user-agent omitted version separator"))?;
    let version = rest
        .split_whitespace()
        .next()
        .filter(|version| !version.is_empty())
        .ok_or_else(|| anyhow!("app-server user-agent omitted version"))?;
    Ok(version.to_string())
}

#[cfg(all(test, unix))]
mod tests {
    use pretty_assertions::assert_eq;

    use super::parse_version_from_user_agent;

    #[test]
    fn parses_version_from_codex_user_agent() {
        assert_eq!(
            parse_version_from_user_agent(
                "codex_app_server_daemon/1.2.3 (Linux 6.8.0; x86_64) codex_cli_rs/1.2.3",
            )
            .expect("version"),
            "1.2.3"
        );

View on GitHub (pinned to 339751715c)

Solutions

  1. Fix the server's user_agent to 'name/x.y.z …' with a real version token first.
  2. Restart the daemon to respawn a managed server with a known-good agent string.
  3. Verify daemon and app-server come from the same codex release.
  4. If the version is genuinely unavailable, decide explicitly how to degrade instead of letting probe abort the lifecycle.

Example fix

// before
user_agent: format!("{}/", name),
// after
user_agent: format!("{}/{}", name, env!("CARGO_PKG_VERSION")),
Defensive patterns

Strategy: fallback

Type guard

fn user_agent_has_version(user_agent: &str) -> bool {
    user_agent
        .split_once('/')
        .and_then(|(_, rest)| rest.split_whitespace().next())
        .is_some_and(|version| !version.is_empty())
}

Try / catch

let info = match probe(&path).await {
    Ok(info) => info,
    Err(err) if err.to_string().contains("user-agent omitted version") => {
        ProbeInfo { app_server_version: "unknown".into() } // degrade, then restart the managed server
    }
    Err(err) => return Err(err),
};

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of openai/codex@339751715c (2026-08-25). Data as JSON: /api/errors/a3417c001c8b65d2. Report an issue: GitHub.