thephpleague/oauth2-server · error · OAuthServerException

invalid_client

invalid_client

Error message

invalid_client

What it means

invalid_client: the client was found but its registered redirect URI is empty, or it is an array with a count other than exactly 1. The auth code grant requires exactly one usable redirect URI, and a CLIENT_AUTHENTICATION_FAILED event is emitted before throwing.

Solutions

  1. Fix the client's stored redirect URI to a single non-empty string
  2. If your entity supports multiple redirect URIs, return exactly one where this grant requires it, or return a single string
  3. Update your ClientRepository/entity mapping so the redirect_uri column is not empty/null
  4. Re-check client seeding/migration scripts for blank redirect_uri values

Example fix

// before (client entity)
$this->redirectUri = []; // or ''
// after
$this->redirectUri = 'https://app.example.com/callback';
Defensive patterns

Strategy: type-guard

Validate before calling

// in your ClientRepository::getClientEntity()
$redirectUri = $clientRecord['redirect_uri'] ?? '';
if (!is_string($redirectUri) || $redirectUri === '') {
    throw new \RuntimeException('Client must have exactly one registered redirect URI');
}

Type guard

function hasUsableRedirectUri($redirectUri): bool {
    if (is_array($redirectUri)) {
        return count($redirectUri) === 1;
    }
    return is_string($redirectUri) && $redirectUri !== '';
}

Try / catch

try {
    $authRequest = $server->validateAuthorizationRequest($request);
} catch (OAuthServerException $e) {
    if ($e->getErrorType() === 'invalid_client') {
        // fix the client record: redirect URI empty or multi-value array
    }
}

Prevention

When it happens

Trigger: validateAuthorizationRequest() reaches the branch where $client->getRedirectUri() === '' or is_array($client->getRedirectUri()) && count(...) !== 1 — i.e. the client entity returned by your ClientRepository has a blank or multi-value (non-single) redirect URI.

Common situations: Custom ClientRepository returning a client entity with redirectUri left as an empty string or an array of several URIs; misconfigured client record in the database; implementing ClientEntityInterface without normalizing the redirect URI.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


AI-assisted analysis of thephpleague/oauth2-server@9d2f6fc0a0 (2026-09-15). Data as JSON: /api/errors/8ac5ef40fcfcdd84. Report an issue: GitHub.

Appendix: source

Thrown at src/Grant/AuthCodeGrant.php:279

        );

        if ($clientId === null) {
            throw OAuthServerException::invalidRequest('client_id');
        }

        $client = $this->getClientEntityOrFail($clientId, $request);

        $redirectUri = $this->getQueryStringParameter('redirect_uri', $request);

        if ($redirectUri !== null) {
            $this->validateRedirectUri($redirectUri, $client, $request);
        } elseif (
            $client->getRedirectUri() === '' ||
            (is_array($client->getRedirectUri()) && count($client->getRedirectUri()) !== 1)
        ) {
            $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));

            throw OAuthServerException::invalidClient($request);
        }

        $stateParameter = $this->getQueryStringParameter('state', $request);

        $scopes = $this->validateScopes(
            $this->getQueryStringParameter('scope', $request, $this->defaultScope),
            $this->makeRedirectUri(
                $redirectUri ?? $this->getClientRedirectUri($client),
                $stateParameter !== null ? ['state' => $stateParameter] : []
            )
        );

        $authorizationRequest = $this->createAuthorizationRequest();
        $authorizationRequest->setGrantTypeId($this->getIdentifier());
        $authorizationRequest->setClient($client);
        $authorizationRequest->setRedirectUri($redirectUri);

        if ($stateParameter !== null) {

View on GitHub (pinned to 9d2f6fc0a0)