{"record":{"id":"e631744f7fa2a8fb","repo":"gitroomhq/postiz-app","slug":"error-invalid-client","errorCode":null,"errorMessage":"{ error: 'invalid_client' }","messagePattern":"\\{ error: 'invalid_client' \\}","errorType":"http","errorClass":"HttpException","httpStatus":401,"severity":"error","filePath":"libraries/nestjs-libraries/src/database/prisma/oauth/oauth.service.ts","lineNumber":257,"sourceCode":"      codeExpiresAt,\n      codeChallenge: pkce?.codeChallenge,\n      codeChallengeMethod: pkce?.codeChallengeMethod,\n      redirectUri: pkce?.redirectUri,\n    });\n\n    return code;\n  }\n\n  async exchangeCodeForToken(\n    code: string,\n    clientId: string,\n    clientSecret?: string,\n    codeVerifier?: string,\n    redirectUri?: string\n  ) {\n    const app = await this._oauthRepository.getAppByClientId(clientId);\n    if (!app) {\n      throw new HttpException(\n        { error: 'invalid_client' },\n        HttpStatus.UNAUTHORIZED\n      );\n    }\n\n    // Public clients (dynamic registration with token_endpoint_auth_method=none)\n    // authenticate with PKCE instead of a client secret\n    const isPublicClient = app.dynamic && app.tokenEndpointAuthMethod === 'none';\n    if (!isPublicClient) {\n      if (\n        !clientSecret ||\n        !app.clientSecret ||\n        app.clientSecret !== AuthService.fixedEncryption(clientSecret)\n      ) {\n        throw new HttpException(\n          { error: 'invalid_client' },\n          HttpStatus.UNAUTHORIZED\n        );","sourceCodeStart":239,"sourceCodeEnd":275,"githubUrl":"https://github.com/gitroomhq/postiz-app/blob/0f1647f7491a217d43eb5ae7a480484bdf0aff3e/libraries/nestjs-libraries/src/database/prisma/oauth/oauth.service.ts#L239-L275","documentation":"Returned as HTTP 401 with OAuth error code invalid_client when exchanging an authorization code for tokens and the client_id does not match any registered OAuth application. This follows RFC 6749 section 5.2 client authentication failure semantics.","triggerScenarios":"POST to the token endpoint with a grant_type=authorization_code request whose client_id is unknown, typo'd, deleted, or from a different environment.","commonSituations":"Copy-paste of the wrong client ID; app deleted or not yet propagated; using production client_id against staging server or vice versa; trailing whitespace in env var.","solutions":["Verify the client_id matches the OAuth app's clientId exactly (no extra spaces/quotes)","Confirm the app exists in the same environment/database you're hitting","If dynamically registered, re-register to obtain a fresh client_id and secret"],"exampleFix":"// before\nconst res = await fetch(tokenEndpoint, { body: new URLSearchParams({ grant_type: 'authorization_code', client_id: 'postiz-prod-abc', code }) });\n// after\nconst res = await fetch(tokenEndpoint, { body: new URLSearchParams({ grant_type: 'authorization_code', client_id: process.env.POSTIZ_CLIENT_ID!.trim(), code }) });","handlingStrategy":"validation","validationCode":"if (!await clientExists(clientId)) { throw new Error('Unknown client_id — check the OAuth app settings'); }","typeGuard":"const hasClientId = (id?: string): id is string => !!id && id.trim().length > 0;","tryCatchPattern":"try { await exchange(code); } catch (e) { if (e?.response?.data?.error === 'invalid_client') { throw new Error('client_id not recognized on this server'); } throw e; }","preventionTips":["Load client_id/secret from typed env config, trimmed","Keep environment-specific client ids separated"],"tags":["oauth2","invalid-client","token-exchange","unauthorized"],"backgroundTag":"oauth-invalid-client","analyzedSha":"0f1647f7491a217d43eb5ae7a480484bdf0aff3e","analyzedAt":"2026-08-27T12:09:55.020Z","schemaVersion":2},"datasetVersion":"2026-08-27T13:17:12.746Z"}