{"record":{"id":"1f67e7bb1dba1b49","repo":"thephpleague/oauth2-server","slug":"invalid-request","errorCode":"invalid_request","errorMessage":"The request is missing a required parameter, is invalid, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed. Check the \"client_id\" parameter","messagePattern":"The request is missing a required parameter, is invalid, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed\\. Check the \"client_id\" parameter","errorType":"http","errorClass":"OAuthServerException","httpStatus":400,"severity":"error","filePath":"src/Grant/AbstractGrant.php","lineNumber":223,"sourceCode":"            || $client->supportsGrantType($grantType) === true;\n    }\n\n    /**\n     * Gets the client credentials from the request from the request body or\n     * the Http Basic Authorization header\n     *\n     * @return array{0:non-empty-string,1:string}\n     *\n     * @throws OAuthServerException\n     */\n    protected function getClientCredentials(ServerRequestInterface $request): array\n    {\n        [$basicAuthUser, $basicAuthPassword] = $this->getBasicAuthCredentials($request);\n\n        $clientId = $this->getRequestParameter('client_id', $request, $basicAuthUser);\n\n        if ($clientId === null) {\n            throw OAuthServerException::invalidRequest('client_id');\n        }\n\n        $clientSecret = $this->getRequestParameter('client_secret', $request, $basicAuthPassword);\n\n        return [$clientId, $clientSecret ?? ''];\n    }\n\n    /**\n     * Validate redirectUri from the request. If a redirect URI is provided\n     * ensure it matches what is pre-registered\n     *\n     * @throws OAuthServerException\n     */\n    protected function validateRedirectUri(\n        string $redirectUri,\n        ClientEntityInterface $client,\n        ServerRequestInterface $request\n    ): void {","sourceCodeStart":205,"sourceCodeEnd":241,"githubUrl":"https://github.com/thephpleague/oauth2-server/blob/9d2f6fc0a0b5aa1bb02506971d3a4ecff2c6526c/src/Grant/AbstractGrant.php#L205-L241","documentation":"Thrown by AbstractGrant::getClientCredentials when neither a 'client_id' request parameter nor an HTTP Basic auth username is present. OAuthServerException::invalidRequest('client_id') produces an HTTP 400 invalid_request error telling the caller the client_id parameter is missing or malformed. The grant cannot even identify the client, so authentication never begins.","triggerScenarios":"POSTing to the token endpoint (respondToAccessTokenRequest) with no client_id in the body and no Authorization: Basic header; sending the credentials in a header your web server strips (e.g. missing mod_rewrite/SetEnvIf Authorization passthrough under Apache+PHP-FPM); sending client_id under a wrong key name (e.g. clientId); sending an empty client_id parameter.","commonSituations":"Apache behind a proxy dropping the Authorization header (very common with PHP); frontend sending JSON body while the endpoint expects form-encoded params; curl examples using -d client_id=... but forgetting the ampersand-separated second param; tests calling the server with a request missing the body params.","solutions":["Include client_id (and client_secret) as application/x-www-form-urlencoded body parameters in the token request, or send them via a proper Authorization: Basic header.","If using Basic auth on Apache, add CGIPassAuth On (or the classic RewriteRule passthrough) so PHP receives the Authorization header.","Check the parameter name is exactly 'client_id' (snake case) and non-empty.","Confirm the request Content-Type is application/x-www-form-urlencoded, not JSON, since getParsedBody won't parse JSON by default."],"exampleFix":"// before\ncurl -X POST https://idp/token -H 'Content-Type: application/json' -d '{\"clientId\":\"abc\"}'\n// after\ncurl -X POST https://idp/token -d 'grant_type=client_credentials&client_id=abc&client_secret=xyz'","handlingStrategy":"validation","validationCode":"// validate the outgoing token request before sending\n$params = ['grant_type' => 'client_credentials', 'client_id' => $clientId, 'client_secret' => $secret];\nforeach (['client_id', 'client_secret'] as $k) {\n    if (!isset($params[$k]) || !is_string($params[$k]) || $params[$k] === '') {\n        throw new \\InvalidArgumentException(\"token request missing {$k}\");\n    }\n}\nhttp_build_query($params); // send as form-encoded body","typeGuard":"function hasClientId(array $parsedBody, ?array $serverParams = null): bool {\n    if (isset($parsedBody['client_id']) && $parsedBody['client_id'] !== '') { return true; }\n    $auth = $serverParams['HTTP_AUTHORIZATION'] ?? '';\n    return str_starts_with($auth, 'Basic ');\n}","tryCatchPattern":"// this error is a 400 invalid_request; catch and give actionable feedback\ntry {\n    $token = $server->respondToAccessTokenRequest($request, $response);\n} catch (OAuthServerException $e) {\n    if ($e->getErrorType() === 'invalid_request' && str_contains($e->getMessage(), 'client_id')) {\n        // caller forgot client_id / Basic header\n    }\n    return $e->generateHttpResponse($response);\n}","preventionTips":["Always send token requests as application/x-www-form-urlencoded.","On Apache/PHP-FPM set CGIPassAuth On so Authorization headers reach PHP.","Centralize token requests in one client helper that asserts required params first.","Check env config for client_id at application boot."],"tags":["oauth2","php","missing-parameter","http-400"],"backgroundTag":"missing-required-argument","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"}