{"record":{"id":"c287096a884f1509","repo":"phacility/phabricator","slug":"oauth-client-s-is-already-untrusted","errorCode":null,"errorMessage":"OAuth client \"%s\" is already untrusted.","messagePattern":"OAuth client \"(.+?)\" is already untrusted\\.","errorType":"console","errorClass":"PhutilArgumentUsageException","httpStatus":null,"severity":"error","filePath":"src/applications/auth/management/PhabricatorAuthManagementUntrustOAuthClientWorkflow.php","lineNumber":46,"sourceCode":"      throw new PhutilArgumentUsageException(\n        pht(\n          'Specify an OAuth client ID with %s.',\n          '--id'));\n    }\n\n    $client = id(new PhabricatorOAuthServerClientQuery())\n      ->setViewer($this->getViewer())\n      ->withIDs(array($id))\n      ->executeOne();\n\n    if (!$client) {\n      throw new PhutilArgumentUsageException(\n        pht(\n          'Failed to find an OAuth client with ID %s.', $id));\n    }\n\n    if (!$client->getIsTrusted()) {\n      throw new PhutilArgumentUsageException(\n        pht(\n          'OAuth client \"%s\" is already untrusted.',\n          $client->getName()));\n    }\n\n    $client->setIsTrusted(0);\n    $client->save();\n\n    $console = PhutilConsole::getConsole();\n    $console->writeOut(\n      \"%s\\n\",\n      pht(\n        'OAuth client \"%s\" is now trusted.',\n        $client->getName()));\n  }\n\n}\n","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/auth/management/PhabricatorAuthManagementUntrustOAuthClientWorkflow.php#L28-L64","documentation":"Thrown by `bin/auth untrust-oauth-client` when the target client's isTrusted flag is already 0. The workflow checks getIsTrusted() and aborts with this PhutilArgumentUsageException before setIsTrusted(0)/save(), so it never performs a redundant write. The database is left exactly as it was.","triggerScenarios":"Running `bin/auth untrust-oauth-client --id N` twice in a row, or untrusting a client that was never trusted in the first place.","commonSituations":"Idempotent deployment scripts re-run the command on every iteration; a security incident runbook is executed after someone already untrusted the client; the admin is uncertain of the current state and probes with the command.","solutions":["Treat the message as confirmation of the desired state — the client is already untrusted, nothing to do.","In scripts, catch PhutilArgumentUsageException containing 'already untrusted' and treat it as success (or check isTrusted in the DB first).","If you expected this client to be trusted, double-check the `--id` — you may be inspecting a different client than intended.","To move it back to trusted, use `bin/auth trust-oauth-client --id N`."],"exampleFix":"// before\n$ bin/auth untrust-oauth-client --id 7   # second run\nOAuth client \"wiki-frontend\" is already untrusted.\n\n// after — check current state instead of assuming\n$ mysql ... -e \"SELECT id,name,isTrusted FROM oauth_server_oauthserverclient WHERE id=7;\"","handlingStrategy":"validation","validationCode":"if (!$client->getIsTrusted()) {\n  // already untrusted: skip the untrust command\n} else {\n  // safe to run: bin/auth untrust-oauth-client --id <id>\n}","typeGuard":null,"tryCatchPattern":"try {\n  // run untrust-oauth-client\n} catch (PhutilArgumentUsageException $ex) {\n  if (strpos($ex->getMessage(), 'already untrusted') !== false) {\n    // desired state already reached; exit 0\n  }\n}","preventionTips":["Check isTrusted in the database (or a prior API call) before invoking.","Treat state-confirmation errors as success in idempotent automation.","Confirm you are acting on the intended client ID when the message surprises you."],"tags":["phabricator","cli","oauth","idempotency","invalid-state"],"backgroundTag":"invalid-state-transition","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}