SonarSource/sonarqube · error · IllegalArgumentException

Failed to decode Github Application private key

Error message

Failed to decode Github Application private key

What it means

Thrown by GithubAppSecurityImpl.readApplicationPrivateKey when the BouncyCastle PemReader parses the provided private key string but no PEM object can be read (parse returns null). The key text is not a valid PEM-encoded block.

Source

Thrown at server/sonar-alm-client/src/main/java/org/sonar/alm/client/github/security/GithubAppSecurityImpl.java:77

    LocalDateTime expiresAt = now.plus(AppToken.EXPIRATION_PERIOD_IN_MINUTES, ChronoUnit.MINUTES);
    ZoneOffset offset = clock.getZone().getRules().getOffset(now);
    Date nowDate = Date.from(now.toInstant(offset));
    Date expiresAtDate = Date.from(expiresAt.toInstant(offset));
    JWTCreator.Builder builder = JWT.create()
      .withIssuer(String.valueOf(appId))
      .withIssuedAt(nowDate)
      .withExpiresAt(expiresAtDate);
    return new AppToken(builder.sign(algorithm));
  }

  private static Algorithm readApplicationPrivateKey(long appId, String encodedPrivateKey) {
    byte[] decodedPrivateKey = encodedPrivateKey.getBytes(UTF_8);
    try (PemReader pemReader = new PemReader(new InputStreamReader(new ByteArrayInputStream(decodedPrivateKey)))) {
      Security.addProvider(new BouncyCastleProvider());

      PemObject pemObject = pemReader.readPemObject();
      if (pemObject == null) {
        throw new IllegalArgumentException("Failed to decode Github Application private key");
      }

      PKCS8EncodedKeySpec keySpec1 = new PKCS8EncodedKeySpec(pemObject.getContent());
      KeyFactory keyFactory = KeyFactory.getInstance("RSA");
      PrivateKey privateKey = keyFactory.generatePrivate(keySpec1);
      return Algorithm.RSA256(new RSAKeyProvider() {
        @Override
        public RSAPublicKey getPublicKeyById(String keyId) {
          throw new UnsupportedOperationException("getPublicKeyById not implemented");
        }

        @Override
        public RSAPrivateKey getPrivateKey() {
          return (RSAPrivateKey) privateKey;
        }

        @Override
        public String getPrivateKeyId() {

View on GitHub (pinned to 184c821202)

Solutions

  1. Paste the complete .pem file contents including '-----BEGIN PRIVATE KEY-----' and '-----END PRIVATE KEY-----' lines
  2. Download the private key again from the GitHub App settings page and reconfigure
  3. Ensure the key was not truncated or re-encoded (e.g. double base64) when stored in the settings

Example fix

// before: privateKey = "MIIEvQIBADANBg..." (raw base64, no PEM headers)
// after: privateKey = "-----BEGIN PRIVATE KEY-----\nMIIEvQ...\n-----END PRIVATE KEY-----";
Defensive patterns

Strategy: validation

Validate before calling

function isValidPem(key) {
  return typeof key === 'string' && /-----BEGIN (RSA )?PRIVATE KEY-----[\s\S]+-----END (RSA )?PRIVATE KEY-----/.test(key.trim());
}

Try / catch

try { configureGithubApp(appId, privateKey); } catch (IllegalArgumentException e) { log("Private key is not valid PEM: " + e.getMessage()); }

Prevention

When it happens

Trigger: readApplicationPrivateKey (via algorithm) receives a key whose bytes contain no recognizable '-----BEGIN ...-----' PEM structure, so pemReader.readPemObject() returns null.

Common situations: Key pasted without the BEGIN/END headers; key stored as raw base64 body only; placeholder or wrong value configured; key mangled by shell escaping or JSON escaping.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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