{"record":{"id":"f53882eafa64a3c9","repo":"SonarSource/sonarqube","slug":"cannot-mint-a-github-installation-token-for-projec","errorCode":null,"errorMessage":"Cannot mint a GitHub installation token for project '%s': invalid GitHub App configuration: %s","messagePattern":"Cannot mint a GitHub installation token for project '(.+?)': invalid GitHub App configuration: (.+?)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":400,"severity":"error","filePath":"server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/github/GithubInstallationTokenProviderImpl.java","lineNumber":128,"sourceCode":"\n  private Optional<GithubInstallationToken> mint(String projectKey, AlmSettingDto almSetting, String almRepo) {\n    String safeProjectKey = sanitizeForLog(projectKey);\n    String safeAlmRepo = sanitizeForLog(almRepo);\n\n    GithubAppConfiguration githubAppConfiguration;\n    try {\n      // TOKEN_MINTING_PERMISSIONS, not the plain default: a minted token is pointless if the GitHub\n      // App doesn't have 'contents: write' to push the remediation commit it's minted for. Most\n      // already-installed apps predate this requirement — there's no in-product way to prompt them\n      // to re-approve, so the wrapped message below is the only guidance an admin gets.\n      githubAppConfiguration = githubGlobalSettingsValidator.validate(almSetting, GithubAppPermissions.TOKEN_MINTING_PERMISSIONS);\n    } catch (IllegalArgumentException e) {\n      // Wrapped (with the project key) rather than swallowed to Optional.empty(): unlike the checks\n      // above, this isn't a \"not bound\" case — the binding exists, its GitHub App configuration is\n      // just broken (bad credentials, missing permissions, unreachable API, ...). Wrapping instead of\n      // rethrowing as-is adds context in one throw (S2139) while still getting the caller a distinct\n      // 400 instead of the same 404 as a genuinely unbound project.\n      throw new IllegalArgumentException(\n        format(\"Cannot mint a GitHub installation token for project '%s': invalid GitHub App configuration: %s\", safeProjectKey, e.getMessage()), e);\n    }\n\n    Optional<Long> installationId = githubApplicationClient.getInstallationId(githubAppConfiguration, almRepo);\n    if (installationId.isEmpty()) {\n      LOG.warn(\"Cannot mint a GitHub installation token for project '{}': GitHub App is not installed on repository '{}'\", safeProjectKey, safeAlmRepo);\n      return Optional.empty();\n    }\n\n    String repositoryName = bareRepositoryName(almRepo);\n    Optional<ExpiringAppInstallationToken> token = githubApplicationClient.createAppInstallationToken(githubAppConfiguration, installationId.get(), repositoryName);\n    if (token.isEmpty()) {\n      LOG.warn(\"Failed to mint a GitHub installation token for project '{}' (repository '{}')\", safeProjectKey, safeAlmRepo);\n      throw new ServerException(HTTP_INTERNAL_ERROR,\n        format(\"Failed to mint a GitHub installation token for project '%s': GitHub App API call failed\", safeProjectKey));\n    }\n\n    return Optional.of(new GithubInstallationToken(","sourceCodeStart":110,"sourceCodeEnd":146,"githubUrl":"https://github.com/SonarSource/sonarqube/blob/184c821202192afc1c599fc912d0889b69fffa53/server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/github/GithubInstallationTokenProviderImpl.java#L110-L146","documentation":"GithubInstallationTokenProviderImpl.mint() wraps IllegalArgumentException from building/validating the GitHub App configuration into an IllegalArgumentException with the project key and the underlying reason, distinguishing 'binding exists but its GitHub App configuration is broken' from 'project not bound'. Callers receive a 400-class error instead of a 404.","triggerScenarios":"Requesting a GitHub installation token for a project whose ALM binding exists but whose GitHub App configuration fails validation: invalid App ID/private key (malformed PEM), wrong client ID, missing permissions, or unreachable GitHub API discovered during configuration setup.","commonSituations":"Rotated or mis-pasted GitHub App private key; App ID changed after re-creating the App; typo in GitHub API URL; private key stored without newline preservation in env/secret manager.","solutions":["Fix the GitHub App configuration in Administration > DevOps Platform Integrations > GitHub: correct App ID, Client ID, and a valid PEM private key","Ensure the private key includes proper PEM headers and newlines (avoid flattening it in env vars)","Verify SonarQube can reach the GitHub API URL (proxy/TLS) and the App is still installed on the target org/repository","Inspect the embedded cause message for the specific validation failure"],"exampleFix":"// before\nGITHUB_PRIVATE_KEY=\"-----BEGIN RSA PRIVATE KEY-----\\n...\"  # literal \\n, invalid PEM\n// after\nstore key verbatim (real newlines) e.g. in a mounted secret file and load it","handlingStrategy":"validation","validationCode":"// sanity-check GitHub App config before minting\nconst pemOk = /-----BEGIN (RSA )?PRIVATE KEY-----[\\s\\S]+-----END (RSA )?PRIVATE KEY-----/.test(privateKey);\nif (!appId || !clientId || !pemOk) throw new Error('GitHub App config incomplete: App ID, Client ID and valid PEM private key required');","typeGuard":"null","tryCatchPattern":"try {\n  const t = githubInstallationTokenProvider.getNewInstallationToken(projectKey);\n} catch (IllegalArgumentException e) {\n  if (e.getMessage().contains(\"invalid GitHub App configuration\")) {\n    // fix App ID / private key / API URL; cause has the detail\n  } else throw e;\n}","preventionTips":["Load the private key from a file/secret preserving PEM newlines","Re-validate integration settings after rotating the GitHub App key","Confirm App installation on the target org before minting tokens","Test the GitHub API URL reachability from the SonarQube host"],"tags":["github","configuration","github-app"],"backgroundTag":"invalid-config-value","analyzedSha":"184c821202192afc1c599fc912d0889b69fffa53","analyzedAt":"2026-09-09T12:23:51.573Z","contentChangedAt":"2026-09-09T12:23:51.573Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}