SonarSource/sonarqube · error · ServerException

Failed to mint a GitHub installation token for project '%s':

Error message

Failed to mint a GitHub installation token for project '%s': GitHub App API call failed

What it means

GithubInstallationTokenProviderImpl.mint() calls githubApplicationClient.createAppInstallationToken(); when the client returns Optional.empty() — the GitHub App API call did not yield a token — it logs a warning and throws ServerException(HTTP_INTERNAL_ERROR, 'Failed to mint ... GitHub App API call failed'). This signals an upstream GitHub API failure rather than a client misconfiguration.

Source

Thrown at server/sonar-webserver-common/src/main/java/org/sonar/server/common/almsettings/github/GithubInstallationTokenProviderImpl.java:142

      // above, this isn't a "not bound" case — the binding exists, its GitHub App configuration is
      // just broken (bad credentials, missing permissions, unreachable API, ...). Wrapping instead of
      // rethrowing as-is adds context in one throw (S2139) while still getting the caller a distinct
      // 400 instead of the same 404 as a genuinely unbound project.
      throw new IllegalArgumentException(
        format("Cannot mint a GitHub installation token for project '%s': invalid GitHub App configuration: %s", safeProjectKey, e.getMessage()), e);
    }

    Optional<Long> installationId = githubApplicationClient.getInstallationId(githubAppConfiguration, almRepo);
    if (installationId.isEmpty()) {
      LOG.warn("Cannot mint a GitHub installation token for project '{}': GitHub App is not installed on repository '{}'", safeProjectKey, safeAlmRepo);
      return Optional.empty();
    }

    String repositoryName = bareRepositoryName(almRepo);
    Optional<ExpiringAppInstallationToken> token = githubApplicationClient.createAppInstallationToken(githubAppConfiguration, installationId.get(), repositoryName);
    if (token.isEmpty()) {
      LOG.warn("Failed to mint a GitHub installation token for project '{}' (repository '{}')", safeProjectKey, safeAlmRepo);
      throw new ServerException(HTTP_INTERNAL_ERROR,
        format("Failed to mint a GitHub installation token for project '%s': GitHub App API call failed", safeProjectKey));
    }

    return Optional.of(new GithubInstallationToken(
      token.get().getValue(), token.get().getExpiresAt().format(ISO_OFFSET_DATE_TIME)));
  }

  /**
   * Strips CR/LF from user-controlled values (project key, ALM repo slug) before logging them, so a
   * crafted value cannot forge extra log lines/entries (CWE-117).
   */
  private static String sanitizeForLog(String value) {
    return CRLF_PATTERN.matcher(value).replaceAll("_");
  }

  /**
   * GitHub's installation-token "repositories" scoping parameter expects the bare repository name
   * (no {@code owner/} prefix), unlike {@code almRepo} which is stored as {@code owner/repo}.

View on GitHub (pinned to 184c821202)

Solutions

  1. Retry the operation after a short backoff — mint failures here are often transient GitHub-side issues
  2. Check GitHub status / GHES health and whether the App is hitting rate limits; reduce mint frequency or add caching of installation tokens
  3. Verify SonarQube-to-GitHub connectivity (proxy, TLS) with a direct API probe using the App credentials
  4. Inspect SonarQube server logs around the WARN line for the underlying HTTP error detail

Example fix

// before
token = client.createAppInstallationToken(cfg, installationId, repo)  // once, no retry
// after
retry with exponential backoff (e.g. 3 attempts) and cache the token until near its expiry
Defensive patterns

Strategy: retry

Validate before calling

// can't fully pre-validate an upstream outage; probe GitHub health first
const probe = await fetch(`${githubApiUrl}/api/v3/rate_limit`, { /* app creds */ }).catch(() => null);
if (!probe || !probe.ok) throw new Error('GitHub API unreachable/rate-limited; defer token minting');

Type guard

null

Try / catch

try {
  const t = githubInstallationTokenProvider.getNewInstallationToken(projectKey);
} catch (ServerException e) {
  if (e.getStatus() == 500 && e.getMessage().contains("GitHub App API call failed")) {
    // exponential backoff retry, then surface as transient
  } else throw e;
}

Prevention

When it happens

Trigger: GitHub API call to create the app installation token fails or returns no token while configuration and installation ID are valid — GitHub 5xx responses, rate limiting on the App, transient network errors between SonarQube and github.com (or GHES).

Common situations: GitHub incidents/outages; secondary rate limits hit by automated token-minting across many repos; GHES under maintenance; proxy timeouts.

Related errors


AI-assisted analysis of SonarSource/sonarqube@184c821202 (2026-09-09). Data as JSON: /api/errors/75aa9c38b74a3548. Report an issue: GitHub.