{"record":{"id":"e851df4e793a004d","repo":"openai/codex","slug":"remote-control-client-revoke-requires-clientid","errorCode":null,"errorMessage":"remote control client revoke requires clientId","messagePattern":"remote control client revoke requires clientId","errorType":"validation","errorClass":"io::Error","httpStatus":null,"severity":"error","filePath":"codex-rs/app-server-transport/src/transport/remote_control/clients.rs","lineNumber":136,"sourceCode":"            .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        })?\n        .push(&params.client_id);\n    let response = send_client_management_request(\n        auth_manager,\n        ClientManagementRequest::Revoke { url: &url },\n        \"revoke remote control client\",\n    )","sourceCodeStart":118,"sourceCodeEnd":154,"githubUrl":"https://github.com/openai/codex/blob/339751715c64496cb86246bfb3935f40e309dd3d/codex-rs/app-server-transport/src/transport/remote_control/clients.rs#L118-L154","documentation":"The second guard in revoke_remote_control_client: after environment_id passes, an empty RemoteControlClientsRevokeParams.client_id is rejected with InvalidInput. The client id is the path segment identifying the enrolled device to delete; without it the request would DELETE the collection path instead of one client.","triggerScenarios":"Invoking revoke with clientId set to an empty string — default-constructed params, a deserialized JSON body missing clientId, or a client record whose id failed to map and coerced to ''.","commonSituations":"Field-name mismatches during serialization (clientID vs clientId); acting on a client entry whose id was null; UI detail panes that never thread the id through.","solutions":["Use the exact client_id from the list-clients response entry being revoked","Check for empty string before sending — presence of the field alone (Some(\"\")) is not enough","Fix field mapping in your data layer if ids arrive empty"],"exampleFix":"// before\nlet params = RemoteControlClientsRevokeParams {\n    environment_id,\n    client_id: String::new(),\n};\nrevoke_remote_control_client(url, &auth_manager, params).await\n// Err: remote control client revoke requires clientId\n\n// after\nlet params = RemoteControlClientsRevokeParams {\n    environment_id,\n    client_id: client.client_id.clone(),\n};\nrevoke_remote_control_client(url, &auth_manager, params).await  // Ok","handlingStrategy":"validation","validationCode":"if (!params.clientId) {\n  throw new Error('clientId is required to revoke a remote control client');\n}","typeGuard":"function hasClientId(p: RemoteControlClientsRevokeParams): p is RemoteControlClientsRevokeParams & { clientId: string } {\n  return typeof p.clientId === 'string' && p.clientId.length > 0;\n}","tryCatchPattern":null,"preventionTips":["Treat empty-string ids as missing when deserializing clients","Pass ids from list responses through unchanged — never regenerate or reformat them","Disable destructive actions until the target id is confirmed non-empty"],"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"}