{"record":{"id":"2a1209fb4c9e2a16","repo":"phacility/phabricator","slug":"expected-s-in-request","errorCode":null,"errorMessage":"Expected '%s' in request!","messagePattern":"Expected '(.+?)' in request!","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/applications/auth/provider/PhabricatorOAuth1AuthProvider.php","lineNumber":85,"sourceCode":"    }\n\n    $denied = $request->getStr('denied');\n    if (strlen($denied)) {\n      // Twitter indicates that the user cancelled the login attempt by\n      // returning \"denied\" as a parameter.\n      throw new PhutilAuthUserAbortedException();\n    }\n\n    // NOTE: You can get here via GET, this should probably be a bit more\n    // user friendly.\n\n    $this->verifyAuthCSRFCode($request, $controller->getExtraURIData());\n\n    $token = $request->getStr('oauth_token');\n    $verifier = $request->getStr('oauth_verifier');\n\n    if (!$token) {\n      throw new Exception(pht(\"Expected '%s' in request!\", 'oauth_token'));\n    }\n\n    if (!$verifier) {\n      throw new Exception(pht(\"Expected '%s' in request!\", 'oauth_verifier'));\n    }\n\n    $adapter->setToken($token);\n    $adapter->setVerifier($verifier);\n\n    $client_code = $this->getAuthCSRFCode($request);\n    $token_secret = $this->loadHandshakeTokenSecret($client_code);\n    $adapter->setTokenSecret($token_secret);\n\n    // NOTE: As a side effect, this will cause the OAuth adapter to request\n    // an access token.\n\n    try {\n      $identifiers = $adapter->getAccountIdentifiers();","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/auth/provider/PhabricatorOAuth1AuthProvider.php#L67-L103","documentation":"Thrown on the final (callback) leg of an OAuth1 handshake in PhabricatorOAuth1AuthProvider. After the user authorizes at the provider, the redirect back to Phabricator must carry an 'oauth_token' query parameter alongside 'oauth_verifier'; Phabricator reads both with $request->getStr('oauth_token') and throws immediately if the token is absent. Without it the request-token secret saved at handshake start cannot be matched, so the handshake cannot proceed.","triggerScenarios":"The provider redirects to the Phabricator auth callback without ?oauth_token=...: the user denied the authorization prompt and the provider sent a denial parameter instead, the user bookmarked/reloaded/manually edited the callback URL, or the provider application's configured callback URL drops query parameters.","commonSituations":"Bitbucket/MediaWiki/JIRA-style OAuth1 apps with a misconfigured callback URL; a user refreshing the callback page after already completing login; starting the handshake in one browser tab and pasting the callback into another.","solutions":["Restart authentication from the Phabricator login screen (/auth/start/) so a fresh handshake with new request token begins","Verify the callback/consumer URL configured in the provider's application settings exactly matches the callback Phabricator shows in the auth provider config","Check the provider's redirect for a denial indicator (e.g. 'denied=' instead of oauth_token) and handle denial as a user-abort rather than a callback","Confirm nothing between the provider and Phabricator (proxy, rewrite rule) strips query string parameters"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Before letting the OAuth1 provider process the callback:\n$token = $request->getStr('oauth_token');\n$verifier = $request->getStr('oauth_verifier');\nif (!strlen($token) || !strlen($verifier)) {\n  // Not a valid provider redirect: treat as aborted handshake,\n  // e.g. redirect back to a fresh /auth/start/ flow instead of fataling.\n  return id(new AphrontRedirectResponse())->setURI('/auth/start/');\n}","typeGuard":null,"tryCatchPattern":"try {\n  $result = $provider->processLoginRequest($request, $controller);\n} catch (Exception $ex) {\n  // Surface pht(\"Expected '%s' in request!\") as a friendly\n  // 'restart authentication' dialog instead of an unhandled fatal.\n  return $this->newDialog()->setTitle(pht('Authentication Failed'))\n    ->appendChild(pht('Restart the login flow and try again.'))\n    ->addSubmitButton(pht('Retry'));\n}","preventionTips":["Never bookmark, reload, or hand-edit the OAuth1 provider callback URL","Configure the provider application's callback URL to exactly what Phabricator's provider config displays","Handle provider denial redirects as user-aborts, not as callbacks missing parameters"],"tags":["oauth1","phabricator","authentication","callback","missing-parameter"],"backgroundTag":"oauth-callback-missing-parameter","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}