thephpleague/oauth2-server · warning · OAuthServerException
slow_down
slow_down
Error message
slow_down
What it means
The device flow client is polling the token endpoint faster than the configured interval. deviceCodePolledTooSoon() compares the device code's lastPolledAt against the grant's deviceAuthInterval (default 5s); when the poll is too soon the server throws OAuthServerException::slowDown() (RFC 8628 slow_down) and the client must back off.
Solutions
- Increase the client's polling interval (and back off further each time slow_down is received, per RFC 8628)
- Respect the interval value returned in the device authorization JSON response
- On the server, tune DeviceCodeGrant deviceAuthInterval to match expected client behavior
- Only update lastPolledAt/persist when appropriate so legitimate first polls are not penalized
Example fix
// before
setInterval(pollToken, 3000);
// after
let interval = deviceAuth.interval * 1000;
setTimeout(async function poll() {
try { await pollToken(); }
catch (e) { if (e.error === 'slow_down') interval += 5000; if (e.error === 'authorization_pending' || e.error === 'slow_down') { setTimeout(poll, interval); return; } }
setTimeout(poll, interval);
}, interval); Defensive patterns
Strategy: retry
Validate before calling
$elapsed = $lastPolledAt ? (time() - $lastPolledAt->getTimestamp()) : PHP_INT_MAX; if ($elapsed < $grantInterval) { sleep($grantInterval - $elapsed); } Try / catch
try { pollToken(); } catch (OAuthServerException $e) { if ($e->getErrorType() === 'slow_down') { $interval += 5; sleep($interval); retry(); } throw $e; } Prevention
- Honor the interval returned in the device authorization response
- Back off exponentially on each slow_down per RFC 8628
- Use jitter to avoid synchronized polling across many devices
- Align server interval with documented client polling behavior
When it happens
Trigger: respondToAccessTokenRequest polling with a device_code whose entity has lastPolledAt set and the elapsed time since it is less than the poll interval; clients ignoring the returned interval from the device authorization response and retrying immediately or in a tight loop.
Common situations: Client polls in a fixed short loop instead of honoring the interval returned with the device code; retrying on authorization_pending without exponential backoff; server interval raised (e.g. setInterval(10)) while clients still poll at 5s.
Related errors
AI-assisted analysis of thephpleague/oauth2-server@9d2f6fc0a0 (2026-09-15).
Data as JSON: /api/errors/3b0b702a2dcf3b25.
Report an issue: GitHub.
Appendix: source
Thrown at src/Grant/DeviceCodeGrant.php:156
*/
public function respondToAccessTokenRequest(
ServerRequestInterface $request,
ResponseTypeInterface $responseType,
DateInterval $accessTokenTTL
): ResponseTypeInterface {
// Validate request
$client = $this->validateClient($request);
$deviceCodeEntity = $this->validateDeviceCode($request, $client);
// If device code has no user associated, respond with pending or slow down
if (is_null($deviceCodeEntity->getUserIdentifier())) {
$shouldSlowDown = $this->deviceCodePolledTooSoon($deviceCodeEntity->getLastPolledAt());
$deviceCodeEntity->setLastPolledAt(new DateTimeImmutable());
$this->deviceCodeRepository->persistDeviceCode($deviceCodeEntity);
if ($shouldSlowDown) {
throw OAuthServerException::slowDown();
}
throw OAuthServerException::authorizationPending();
}
if ($deviceCodeEntity->getUserApproved() === false) {
throw OAuthServerException::accessDenied();
}
// Finalize the requested scopes
$finalizedScopes = $this->scopeRepository->finalizeScopes($deviceCodeEntity->getScopes(), $this->getIdentifier(), $client, $deviceCodeEntity->getUserIdentifier());
// Issue and persist new access token
$accessToken = $this->issueAccessToken($accessTokenTTL, $client, $deviceCodeEntity->getUserIdentifier(), $finalizedScopes);
$this->getEmitter()->emit(new RequestAccessTokenEvent(RequestEvent::ACCESS_TOKEN_ISSUED, $request, $accessToken));
$responseType->setAccessToken($accessToken);
// Issue and persist new refresh token if givenView on GitHub (pinned to 9d2f6fc0a0)