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
- Derive the id from the actual key material you inject: gpg --list-packets on the exported armored key shows key IDs
- Re-sync CI variables so keyId and secretKey come from the same key export
- If the data holds exactly one key, use the form without a keyId so the primary key is taken implicitly
- 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
- Treat keyId + secretKey + password as one rotated unit, stored together
- When the key data has exactly one key, omit the keyId so the primary key is used implicitly
- Add a pipeline step that runs gpg --list-packets on the secret before the build
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
- did not find secret key for id '{}' in key source '{}'
- Unable to generate signature for '%s' as no signatory is ava
- Unable to generate signature for '%s' as no signature type h
- Invalid args ({}: {})
- Unable to retrieve secret key from key ring file '{}' as it
AI-assisted analysis of gradle/gradle@534f27719b (2026-08-22).
Data as JSON: /api/errors/b25a6033e74f4334.
Report an issue: GitHub.