{"record":{"id":"38ccac20c7e0b27a","repo":"thephpleague/oauth2-server","slug":"invalid-scope","errorCode":"invalid_scope","errorMessage":"The requested scope is invalid, unknown, or malformed","messagePattern":"The requested scope is invalid, unknown, or malformed","errorType":"http","errorClass":"OAuthServerException","httpStatus":400,"severity":"error","filePath":"src/Grant/AbstractGrant.php","lineNumber":273,"sourceCode":"     * @throws OAuthServerException\n     *\n     * @return ScopeEntityInterface[]\n     */\n    public function validateScopes(string|array|null $scopes, ?string $redirectUri = null): array\n    {\n        if ($scopes === null) {\n            $scopes = [];\n        } elseif (is_string($scopes)) {\n            $scopes = $this->convertScopesQueryStringToArray($scopes);\n        }\n\n        $validScopes = [];\n\n        foreach ($scopes as $scopeItem) {\n            $scope = $this->scopeRepository->getScopeEntityByIdentifier($scopeItem);\n\n            if ($scope instanceof ScopeEntityInterface === false) {\n                throw OAuthServerException::invalidScope($scopeItem, $redirectUri);\n            }\n\n            $validScopes[] = $scope;\n        }\n\n        return $validScopes;\n    }\n\n    /**\n     * Converts a scopes query string to an array to easily iterate for validation.\n     *\n     * @return string[]\n     */\n    private function convertScopesQueryStringToArray(string $scopes): array\n    {\n        return array_filter(explode(self::SCOPE_DELIMITER_STRING, trim($scopes)), static fn ($scope) => $scope !== '');\n    }\n","sourceCodeStart":255,"sourceCodeEnd":291,"githubUrl":"https://github.com/thephpleague/oauth2-server/blob/9d2f6fc0a0b5aa1bb02506971d3a4ecff2c6526c/src/Grant/AbstractGrant.php#L255-L291","documentation":"Thrown when a requested OAuth scope identifier does not resolve to a registered scope entity. The grant calls ScopeRepository::getScopeEntityByIdentifier() for every scope in the request; if the repository returns null (or a non-ScopeEntityInterface value), the scope is rejected as invalid, unknown, or malformed. This enforces that only scopes the server explicitly defines can be granted.","triggerScenarios":"Any grant flow (authorization code, access token, refresh token, device code) where the request's scope parameter contains an identifier that the application's scope repository does not recognize, e.g. 'scope=email profile' where 'profile' has no scope entity registered.","commonSituations":"Developers forget to implement/return entities in getScopeEntityByIdentifier, change default scopes in a client without registering them server-side, copy scope names from another project, or rename scopes during a refactor so stored client scopes no longer match the repository.","solutions":["Implement (or fix) ScopeRepository::getScopeEntityByIdentifier to return a ScopeEntityInterface instance for every scope identifier your server supports","Check the exact scope string in the failing request against the identifiers your repository recognizes (watch for typos, extra whitespace, or unsupported scopes)","If the scope is legitimate, register it in the repository before clients request it","If the scope is not needed, remove it from the client's configured/requested scopes"],"exampleFix":"// before\npublic function getScopeEntityByIdentifier($identifier)\n{\n    return null; // always null -> every scope throws invalid_scope\n}\n// after\npublic function getScopeEntityByIdentifier($identifier)\n{\n    $scopes = ['basic', 'email', 'profile'];\n    if (in_array($identifier, $scopes, true)) {\n        $scope = new ScopeEntity();\n        $scope->setIdentifier($identifier);\n        return $scope;\n    }\n    return null;\n}","handlingStrategy":"validation","validationCode":"const requested = new URLSearchParams(body).get('scope')?.split(' ') ?? [];\nconst supported = new Set(['basic', 'email', 'profile']);\nconst unsupported = requested.filter(s => !supported.has(s));\nif (unsupported.length) throw new Error(`Unsupported scopes: ${unsupported.join(', ')}`);","typeGuard":"function isRegisteredScope(scope: string, registry: Map<string, ScopeEntity>): boolean { return registry.has(scope.trim()); }","tryCatchPattern":"try {\n  await authorize({ scope: requestedScopes.join(' ') });\n} catch (e) {\n  if (e.code === 'invalid_scope') {\n    console.error(`Scope rejected: ${e.hint ?? e.message}`);\n  }\n  throw e;\n}","preventionTips":["Register every scope your clients may request in the scope repository","Log the exact scope string from failing requests to spot typos/whitespace","Keep client-configured default scopes in sync with server-side scope definitions","Trim and normalize scope strings before sending"],"tags":["oauth2","scope","validation","configuration"],"backgroundTag":"invalid-enum-value","analyzedSha":"9d2f6fc0a0b5aa1bb02506971d3a4ecff2c6526c","analyzedAt":"2026-09-15T22:33:30.452Z","contentChangedAt":"2026-09-15T22:33:30.452Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}