{"record":{"id":"b98fc544c5466564","repo":"openai/codex","slug":"remote-control-client-revoke-requires-environmenti","errorCode":null,"errorMessage":"remote control client revoke requires environmentId","messagePattern":"remote control client revoke requires environmentId","errorType":"validation","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"codex-rs/app-server-transport/src/transport/remote_control/clients.rs","lineNumber":130,"sourceCode":"        },\n    )?;\n    Ok(RemoteControlClientsListResponse {\n        data: response\n            .items\n            .into_iter()\n            .map(RemoteControlClient::try_from)\n            .collect::<io::Result<_>>()?,\n        next_cursor: response.cursor,\n    })\n}\n\npub(super) async fn revoke_remote_control_client(\n    remote_control_url: &str,\n    auth_manager: &Arc<AuthManager>,\n    params: RemoteControlClientsRevokeParams,\n) -> io::Result<RemoteControlClientsRevokeResponse> {\n    if params.environment_id.is_empty() {\n        return Err(io::Error::new(\n            ErrorKind::InvalidInput,\n            \"remote control client revoke requires environmentId\",\n        ));\n    }\n    if params.client_id.is_empty() {\n        return Err(io::Error::new(\n            ErrorKind::InvalidInput,\n            \"remote control client revoke requires clientId\",\n        ));\n    }\n    let mut url = environment_clients_url(remote_control_url, &params.environment_id)?;\n    url.path_segments_mut()\n        .map_err(|()| {\n            io::Error::new(\n                ErrorKind::InvalidInput,\n                \"remote control URL cannot be a base\",\n            )\n        })?","sourceCodeStart":112,"sourceCodeEnd":148,"githubUrl":"https://github.com/openai/codex/blob/339751715c64496cb86246bfb3935f40e309dd3d/codex-rs/app-server-transport/src/transport/remote_control/clients.rs#L112-L148","documentation":"revoke_remote_control_client first validates that RemoteControlClientsRevokeParams.environment_id is non-empty, failing fast with InvalidInput before constructing the DELETE URL. Revocation targets one client inside one environment, so the environment scope is mandatory.","triggerScenarios":"Calling the client revoke RPC with a blank environmentId — reusing params whose field was never set, or a UI revoking a client after the active environment context was cleared.","commonSituations":"Client selected from a stale list after environment context reset; JSON payload built with only clientId; multi-environment dashboards losing track of which environment the client belongs to.","solutions":["Pass the same environmentId that produced the client list the selection came from","Populate both environmentId and clientId from the selected client's owning context before invoking revoke","Validate the request payload client-side before sending"],"exampleFix":"// before\nlet params = RemoteControlClientsRevokeParams {\n    environment_id: String::new(),\n    client_id: client.client_id.clone(),\n};\nrevoke_remote_control_client(url, &auth_manager, params).await\n// Err: remote control client revoke requires environmentId\n\n// after\nlet params = RemoteControlClientsRevokeParams {\n    environment_id: environment_id.clone(),\n    client_id: client.client_id.clone(),\n};\nrevoke_remote_control_client(url, &auth_manager, params).await  // Ok","handlingStrategy":"validation","validationCode":"fn can_revoke(p: &RemoteControlClientsRevokeParams) -> bool {\n    !p.environment_id.is_empty() && !p.client_id.is_empty()\n}","typeGuard":"function isRevokeReady(p: RemoteControlClientsRevokeParams): p is RemoteControlClientsRevokeParams & { environmentId: string; clientId: string } {\n  return typeof p.environmentId === 'string' && p.environmentId.length > 0\n      && typeof p.clientId === 'string' && p.clientId.length > 0;\n}","tryCatchPattern":null,"preventionTips":["Enable revoke actions only when a selected {environmentId, clientId} pair is complete","Return the environmentId alongside each listed client so consumers never guess it","Reject empty strings at the API gateway with a 400 before they reach the transport"],"tags":["validation","remote-control","revocation","api-params","invalid-input"],"backgroundTag":"missing-required-parameter","analyzedSha":"339751715c64496cb86246bfb3935f40e309dd3d","analyzedAt":"2026-08-25T05:35:09.876Z","schemaVersion":2},"datasetVersion":"2026-08-25T06:17:31.827Z"}