apache/beam · error · IllegalArgumentException
Both clientCertPath and clientCertKeyPath must be specified
Error message
Both clientCertPath and clientCertKeyPath must be specified together.
What it means
When configuring client-side TLS certificates for Spanner (e.g. for a emulator or proxy), the cert path and the cert key path must be supplied as a pair. checkMandatoryFields() throws this IllegalArgumentException when exactly one of clientCertPath/clientCertKeyPath is non-null, since a one-sided configuration can never produce a usable key store.
Source
Thrown at sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/spanner/SpannerTransformRegistrar.java:134
this.clientCertPath = clientCertPath;
}
public void setClientCertKeyPath(@Nullable String clientCertKeyPath) {
this.clientCertKeyPath = clientCertKeyPath;
}
void checkMandatoryFields() {
if (projectId.isEmpty()) {
throw new IllegalArgumentException("projectId can't be empty");
}
if (databaseId.isEmpty()) {
throw new IllegalArgumentException("databaseId can't be empty");
}
if (instanceId.isEmpty()) {
throw new IllegalArgumentException("instanceId can't be empty");
}
if ((clientCertPath != null) != (clientCertKeyPath != null)) {
throw new IllegalArgumentException(
"Both clientCertPath and clientCertKeyPath must be specified together.");
}
}
}
public static class ReadBuilder
implements ExternalTransformBuilder<ReadBuilder.Configuration, PBegin, PCollection<Row>> {
public static class Configuration extends CrossLanguageConfiguration {
// TODO: https://github.com/apache/beam/issues/20415 Come up with something to determine
// schema without this explicit parameter
private Schema schema = Schema.builder().build();
private @Nullable String sql;
private @Nullable String table;
private @Nullable Boolean batching;
private @Nullable String timestampBoundMode;
private @Nullable String readTimestamp;
private @Nullable String timeUnit;View on GitHub (pinned to 12126d8942)
Solutions
- Always call both .withClientCertPath(certPath) and .withClientCertKeyPath(keyPath) with non-null values.
- If you don't need mTLS, remove both setters so neither is set.
- Check your config/secret store contains both the certificate and its key.
Example fix
// before
builder.withClientCertPath("/certs/client.pem")
// after
builder.withClientCertPath("/certs/client.pem").withClientCertKeyPath("/certs/client.key") Defensive patterns
Strategy: validation
Validate before calling
if ((clientCertPath == null) != (clientCertKeyPath == null)) { throw new IllegalArgumentException("clientCertPath and clientCertKeyPath must be set together"); } Try / catch
try { builder.withClientCertPath(cert).withClientCertKeyPath(key); } catch (IllegalArgumentException e) { throw new ConfigException("mTLS config incomplete: " + e.getMessage(), e); } Prevention
- Wrap cert/key into a single config object so they are always set together.
- Check secret mounting includes both files before launching the pipeline.
- Prefer the default transport (no explicit certs) unless mTLS to a proxy/emulator is required.
When it happens
Trigger: Calling withClientCertPath(path) without withClientCertKeyPath(keyPath), or vice versa, on the Spanner builder/configuration; one of the two values null because a config file only defined one entry.
Common situations: Setting up Spanner against an emulator/mtls proxy where docs mention both values but only the cert was wired in; templated configs where the key path secret was not mounted; refactoring that dropped one of the two setters.
Understand the failure class
Background: Conflicting config options: "cannot be used together" — configuration validation errors across open-source libraries — this error's family across 162 libraries.
Related errors
- databaseId can't be empty
- instanceId can't be empty
- Query and table params are mutually exclusive. Set just one
- Schema can't be empty
- ChangeStreamName can't be empty
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/210155dfb1427ab6.
Report an issue: GitHub.