openai/codex · error · std::io::Error

invalid remote control account id header: {err}

Error message

invalid remote control account id header: {err}

What it means

Raised while building request headers for remote-control management traffic: the stored ChatGPT account id is not a legal HTTP header value, so HeaderValue::from_str fails and request_headers wraps it in an io::Error of kind InvalidInput. The id becomes the chatgpt-account-id header on every list/revoke/pairing request, so any control character, newline, or otherwise invalid byte in the stored id breaks all remote-control calls before they are sent.

Source

Thrown at codex-rs/app-server-transport/src/transport/remote_control/auth.rs:27

use tokio::sync::watch;
use tracing::info;
use tracing::warn;

pub(super) const REMOTE_CONTROL_ACCOUNT_ID_HEADER: &str = "chatgpt-account-id";

pub(super) struct RemoteControlConnectionAuth {
    pub(super) auth_provider: SharedAuthProvider,
    pub(super) account_id: String,
}

impl RemoteControlConnectionAuth {
    pub(super) fn request_headers(&self) -> io::Result<HeaderMap> {
        let mut headers = HeaderMap::new();
        self.auth_provider.add_auth_headers(&mut headers);
        headers.insert(
            REMOTE_CONTROL_ACCOUNT_ID_HEADER,
            HeaderValue::from_str(&self.account_id).map_err(|err| {
                io::Error::new(
                    ErrorKind::InvalidInput,
                    format!("invalid remote control account id header: {err}"),
                )
            })?,
        );
        Ok(headers)
    }
}

pub(super) async fn load_remote_control_auth(
    auth_manager: &Arc<AuthManager>,
) -> io::Result<RemoteControlConnectionAuth> {
    let mut reloaded = false;
    let auth = loop {
        let Some(auth) = auth_manager.auth().await else {
            if reloaded {
                return Err(io::Error::new(
                    ErrorKind::PermissionDenied,

View on GitHub (pinned to 339751715c)

Solutions

  1. Inspect the ChatGPT account id in CODEX_HOME/auth.json for stray newlines, control characters, or invisible bytes
  2. Re-run codex login (Sign in with ChatGPT) so the server writes a clean account id
  3. If the id originates upstream, strip/validate it to header-safe ASCII before persisting it

Example fix

// before: auth.json
"chatgpt_account_id": "acct-123\n"
let headers = auth.request_headers()?; // Err: invalid remote control account id header

// after: auth.json
"chatgpt_account_id": "acct-123"
let headers = auth.request_headers()?; // Ok: sends chatgpt-account-id: acct-123
Defensive patterns

Strategy: validation

Validate before calling

use axum::http::HeaderValue;

fn account_id_is_header_safe(id: &str) -> bool {
    HeaderValue::from_str(id).is_ok()
}
// run after loading auth, before the first remote-control request

Try / catch

Match io::Error where kind() == InvalidInput and the message starts with 'invalid remote control account id header'; surface it as corrupt login data (offer re-login). Retrying unchanged cannot succeed — the same stored id fails every time.

Prevention

When it happens

Trigger: request_headers() runs on every remote-control HTTP request (send_client_management_request_once, send_remote_control_server_request, and the pairing/enrollment flows) and fails when the account id from auth contains bytes invalid in a header value — e.g. an embedded newline as in the repo test 'invalid\naccount', other control characters, or any value HeaderValue::from_str rejects.

Common situations: Hand-edited or corrupted CODEX_HOME/auth.json with a stray newline or whitespace in the account id field; account ids pasted from elsewhere; auth files mangled by provisioning scripts or line-ending conversion that introduced CRLF into the id.

Related errors


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