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

remote control requires ChatGPT authentication

Error message

remote control requires ChatGPT authentication

What it means

load_remote_control_auth found no auth at all: AuthManager.auth() returned None, one automatic reload was attempted, and it was still None. Remote-control endpoints (pairing, enrollment, client list/revoke, preference persistence) act as the signed-in ChatGPT user, so the call is rejected up front with PermissionDenied instead of sending an unauthenticated request.

Source

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

            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,
                    "remote control requires ChatGPT authentication",
                ));
            }
            auth_manager.reload().await;
            reloaded = true;
            continue;
        };
        if !auth.uses_codex_backend() {
            break auth;
        }
        if auth.get_account_id().is_none() && !reloaded {
            auth_manager.reload().await;
            reloaded = true;
            continue;
        }
        break auth;
    };

View on GitHub (pinned to 339751715c)

Solutions

  1. Run codex login and choose Sign in with ChatGPT, then retry the remote-control call
  2. Verify CODEX_HOME resolves to the directory that contains auth.json (ls $CODEX_HOME/auth.json)
  3. In tests/CI, provision an auth.json fixture or inject a mock AuthManager before invoking remote-control APIs
  4. Confirm no env/config override redirects the home directory so the reload finds the fresh login

Example fix

// before
$ ls "$CODEX_HOME/auth.json"  # No such file or directory
list_remote_control_clients(url, &auth_manager, params).await
// Err: remote control requires ChatGPT authentication

// after
$ codex login   # choose 'Sign in with ChatGPT'
list_remote_control_clients(url, &auth_manager, params).await  // Ok
Defensive patterns

Strategy: validation

Validate before calling

async fn has_auth(auth_manager: &Arc<AuthManager>) -> bool {
    auth_manager.auth().await.is_some()
}
// gate every remote-control call on this; if false, prompt login first

Try / catch

Catch io::Error with kind() == PermissionDenied and message 'remote control requires ChatGPT authentication'; route the user to login. Do not loop-retry — the library already performed one reload internally before giving up.

Prevention

When it happens

Trigger: Any remote-control entry point — start_pairing, pairing_status, enroll_pairing_server, refresh_pairing_enrollment, persist_preference, or list/revoke clients via send_client_management_request — when no usable auth is stored (missing or empty CODEX_HOME/auth.json) and the internal single reload does not fix it.

Common situations: Fresh machine or container where codex login was never run; CODEX_HOME pointing at the wrong or an empty directory in CI; auth.json deleted or corrupted after logout; headless servers expected to be pre-provisioned but were not.

Understand the failure class

Related errors


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