gradle/gradle · error · InvalidUserDataException

Cannot find key with id '%s' in key data

Error message

Cannot find key with id '%s' in key data

What it means

PgpSignatoryUtil.parseSecretKey parses in-memory key data (the string form used by in-memory signatories). When a keyId is supplied, it searches the parsed ring for that id; no match throws InvalidUserDataException 'Cannot find key with id ... in key data'. The key data parsed fine but does not contain the requested key.

Source

Thrown at platforms/software/signing/src/main/java/org/gradle/plugins/signing/signatory/internal/pgp/PgpSignatoryUtil.java:178

     * The {@code keyData} may contain one or more master keys with subkeys.
     * If there is more than one master key or a subkey is needed, the {@code keyId} must be provided.
     * If omitted, the keyData is treated as a single master key (with subkeys) and the master secret key is returned.
     *
     * @param keyId the id of the key, can be null to return the only master secret key
     * @param keyData the ASCII-armored representation of the key
     * @return the parsed secret key
     */
    public static PGPSecretKey parseSecretKey(@Nullable String keyId, String keyData) {
        try (InputStream in = PGPUtil.getDecoderStream(new ByteArrayInputStream(keyData.getBytes(UTF_8)))) {
            if (keyId == null) {
                return new JcaPGPSecretKeyRing(in).getSecretKey();
            } else {
                PgpKeyId expectedKeyId = new PgpKeyId(keyId);
                PGPSecretKey key = findSecretKey(new JcaPGPSecretKeyRingCollection(in), expectedKeyId);
                if (key != null) {
                    return key;
                }
                throw new InvalidUserDataException(String.format("Cannot find key with id '%s' in key data",  keyId));
            }
        } catch (IOException | PGPException e) {
            throw new InvalidUserDataException("Could not read PGP secret key", e);
        }
    }
}

View on GitHub (pinned to 534f27719b)

Solutions

  1. Derive the id from the actual key material you inject: gpg --list-packets on the exported armored key shows key IDs
  2. Re-sync CI variables so keyId and secretKey come from the same key export
  3. If the data holds exactly one key, use the form without a keyId so the primary key is taken implicitly
  4. Verify the id format: 8 hex chars or 0x + 8 hex chars

Example fix

# before
signing {
    inMemoryPgp 'release', '1A2B3C4D', secretKey, password // id not in secretKey
}

# after
gpg --list-packets secretKey.asc | grep 'keyid' # e.g. keyid 00B5050F
signing {
    inMemoryPgp 'release', '00B5050F', secretKey, password
}
Defensive patterns

Strategy: try-catch

Validate before calling

def ids = [] as Set
new BcPGPSecretKeyRingCollection(PGPUtil.getDecoderStream(new ByteArrayInputStream(keyData.bytes))).keyRings.each { r ->
    r.secretKeys.each { k -> ids << PgpKeyId.toHex(k.keyID) }
}
assert keyId in ids : "keyId ${keyId} not in key data; found ${ids}"

Try / catch

try {
    def key = PgpSignatoryUtil.parseSecretKey(keyId, keyData)
} catch (InvalidUserDataException e) {
    throw new GradleException("keyId does not match the injected secretKey; re-sync CI variables", e)
}

Prevention

When it happens

Trigger: useInMemoryPgpKeys(keyId, secretKey, password) where keyId does not match any (sub)key inside secretKey; using the long 64-bit id while the data's primary key has a different short id; trailing whitespace altering the id string.

Common situations: CI secrets drift: the key env var was rotated but the key id variable was not; copying a key id from documentation instead of from the actual exported key; multiple subkeys causing id confusion.

Related errors


AI-assisted analysis of gradle/gradle@534f27719b (2026-08-22). Data as JSON: /api/errors/b25a6033e74f4334. Report an issue: GitHub.