apache/beam · error · IllegalStateException
private key cannot be null
Error message
private key cannot be null
What it means
When BigtableIO credentials are configured with a service account JSON key file, the code loads the key file into a PKCS12 KeyStore and extracts the 'privatekey' entry. If keyStore.getKey(...) returns null, the key file did not contain the expected private key alias, so an IllegalStateException('private key cannot be null') is thrown. This indicates the supplied file is not a valid Google service account PKCS12 key file.
Solutions
- Ensure the key file is a .p12 (PKCS12) Google service account key, not a JSON key file; download the .p12 key from Cloud Console if needed
- Prefer Application Default Credentials or UserSuppliedCredentialOptions instead of a raw key file
- Verify the key file is not corrupted (open it with `keytool -list -keystore file.p12 -storepass notasecret` and confirm the 'privatekey' alias)
- Re-create the service account key if the file predates key format changes or was hand-modified
Example fix
// before
.withCredentialOptions(BeamBigtableIO.CredentialOptions.serviceAccountCredentialOptions("sa.json", null))
// after
.withCredentialOptions(BeamBigtableIO.CredentialOptions.serviceAccountCredentialOptions("sa.p12", serviceAccount)) Defensive patterns
Strategy: validation
Validate before calling
File f = new File(keyPath);
try (FileInputStream fin = new FileInputStream(f)) {
KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(fin, "notasecret".toCharArray());
if (ks.getKey("privatekey", "notasecret".toCharArray()) == null) {
throw new IllegalArgumentException("Not a valid service account .p12: 'privatekey' alias missing");
}
} Prevention
- Prefer JSON key files with GoogleCredentials or Application Default Credentials over raw .p12 keystore options
- Verify key files with keytool before using them in pipelines
- Never pass JSON-format key files where a PKCS12 keystore is expected
When it happens
Trigger: CredentialOptions.ServiceModelCredentialOptions with a key file path pointing to a file that is not a valid PKCS12 service account key, or a key file whose 'privatekey' alias entry is absent/renamed; also occurs if the keystore password differs from Google's fixed 'notasecret'.
Common situations: Passing a JSON-format service account key file where the translator expects a .p12 file; passing a non-credential file path; using a key file exported/converted in a way that drops the 'privatekey' alias; stale key files from old project setups.
Related errors
- exception while retrieving credentials
- Bigtable location must be in the following format…
- Can't load the client certificate from the keystore
- columnsMapping ' ' does not fit to schema field names
- Could not create a temporary directory for storing…
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/9d4599bb60b719b6.
Report an issue: GitHub.
Appendix: source
Thrown at sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/bigtable/BigtableConfigTranslator.java:423
switch (credOptions.getCredentialType()) {
case DefaultCredentials:
// Veneer uses default credentials, so no need to reset here
break;
case P12:
String keyFile = ((CredentialOptions.P12CredentialOptions) credOptions).getKeyFile();
String serviceAccount =
((CredentialOptions.P12CredentialOptions) credOptions).getServiceAccount();
try {
KeyStore keyStore = KeyStore.getInstance("PKCS12");
try (FileInputStream fin = new FileInputStream(keyFile)) {
keyStore.load(fin, "notasecret".toCharArray());
}
PrivateKey privateKey =
(PrivateKey) keyStore.getKey("privatekey", "notasecret".toCharArray());
if (privateKey == null) {
throw new IllegalStateException("private key cannot be null");
}
Credentials credentials =
ServiceAccountJwtAccessCredentials.newBuilder()
.setClientEmail(serviceAccount)
.setPrivateKey(privateKey)
.build();
builder.setCredentialFactory(FixedCredentialFactory.create(credentials));
} catch (GeneralSecurityException exception) {
throw new RuntimeException("exception while retrieving credentials", exception);
}
break;
case SuppliedCredentials:
Credentials credentials =
((CredentialOptions.UserSuppliedCredentialOptions) credOptions).getCredential();
builder.setCredentialFactory(FixedCredentialFactory.create(credentials));
break;
case SuppliedJson:
CredentialOptions.JsonCredentialsOptions jsonCredentialsOptions =View on GitHub (pinned to 12126d8942)