openai/codex · error · anyhow::Error
{method} rejected legacy params
Error message
{method} rejected legacy params What it means
The app-server-daemon remote-control client calls remoteControl/enable or remoteControl/disable twice: first with a params payload such as {"ephemeral":true}, then, if the server answers JSON-RPC -32602, once more with params omitted for older servers (request_remote_control_with_legacy_fallback). This error means the second, legacy-shaped attempt was also rejected with Invalid params, so the connected app-server accepts neither parameter shape and the RPC cannot proceed.
Source
Thrown at codex-rs/app-server-daemon/src/remote_control_client.rs:185
Some(params),
)
.await?;
match read_remote_control_response(websocket, &REMOTE_CONTROL_REQUEST_ID, method).await? {
RemoteControlRpcResponse::Success(response) => Ok(response),
RemoteControlRpcResponse::InvalidParams => {
send_remote_control_request(
websocket,
REMOTE_CONTROL_REQUEST_ID.clone(),
method,
/*params*/ None,
)
.await?;
match read_remote_control_response(websocket, &REMOTE_CONTROL_REQUEST_ID, method)
.await?
{
RemoteControlRpcResponse::Success(response) => Ok(response),
RemoteControlRpcResponse::InvalidParams => {
Err(anyhow!("{method} rejected legacy params"))
}
}
}
}
}
async fn connect_with_retry(
socket_path: &Path,
connect_timeout: Duration,
connect_retry_delay: Duration,
) -> Result<WebSocketStream<codex_uds::UnixStream>> {
let deadline = Instant::now() + connect_timeout;
loop {
match client::connect(socket_path).await {
Ok(websocket) => return Ok(websocket),
Err(_) if Instant::now() < deadline => {
sleep(connect_retry_delay).await;
}View on GitHub (pinned to 339751715c)
Solutions
- Align versions: reinstall or update codex so the daemon and the app-server binary come from the same release, then retry; mismatched param schemas between the two are the dominant cause.
- Identify the serving binary: read the userAgent in the initialize response (for example codex_app_server/1.2.3) and compare it with the daemon version.
- If you control the server or a test mock, implement the fallback contract: answer -32602 to the {"ephemeral":true} request and a valid response to the follow-up request with params null, as the tests in remote_control_client.rs do.
- If versions match and it persists, capture the websocket frames to log the server's actual InvalidParams message and report it against the app-server remoteControl handler.
Example fix
// before (mock server): always answers -32602
if method == "remoteControl/disable" {
reply_error(id, -32602, "Invalid params"); // daemon fails: rejected legacy params
}
// after (mock server): reject params once, then answer the param-less retry
if method == "remoteControl/disable" && req.params.is_some() {
reply_error(id, -32602, "Invalid params");
} else if method == "remoteControl/disable" {
reply_result(id, disable_response()); // legacy shape accepted
} Defensive patterns
Strategy: try-catch
Validate before calling
// After client::initialize, compare the server's reported version with the
// daemon's before issuing remoteControl RPCs (sketch):
let ua = initialize_result["userAgent"].as_str().unwrap_or_default();
if !same_release(ua, daemon_version()) {
return Err(anyhow!("app-server {ua} differs from daemon {}; align installs", daemon_version()));
} Try / catch
match enable_remote_control(&socket_path).await {
Ok(status) => { /* proceed */ }
Err(err) if err.to_string().contains("rejected legacy params") => {
// Both param shapes refused: treat as version skew and surface an
// actionable error instead of retrying the same request.
return Err(err.context("app-server rejected every remoteControl param shape; daemon and app-server versions likely differ"));
}
Err(err) => return Err(err),
} Prevention
- Keep a single codex installation on PATH and under ~/.codex so daemon and app-server binaries never diverge.
- In test doubles, implement the full fallback contract: reject the parametrized request, then answer the param-less retry.
- Read the initialize response userAgent before issuing remoteControl RPCs to unknown server builds.
- Drive remoteControl param shapes from codex-app-server-protocol types instead of hand-rolled JSON across versions.
When it happens
Trigger: Invoking disable_remote_control or enable_remote_control_with_timeout (and its wrappers enable_remote_control / enable_remote_control_with_connect_retry) against an app-server whose remoteControl handler returns -32602 for both the parametrized request and the param-less retry: a build with a different remoteControl params schema, a much older or newer codex binary than the daemon, or a test double that unconditionally replies InvalidParams.
Common situations: Version skew between the codex CLI and the app-server it spawns (npm global vs ~/.codex/bin mixed installs); partial upgrades where one binary updated and the other did not; integration-test mock servers that answer -32602 without implementing the retry-then-succeed fallback contract.
Related errors
- {method} failed: {}
- app-server user-agent omitted version separator
- app-server closed the control socket
- app-server user-agent omitted version
- invalid remote control account id header: {err}
AI-assisted analysis of openai/codex@339751715c (2026-08-25).
Data as JSON: /api/errors/d5655d96519aebce.
Report an issue: GitHub.