apache/beam · error · IllegalArgumentException
Both PubsubTestClientFactory and Clock need to be specified…
Error message
Both PubsubTestClientFactory and Clock need to be specified for testing, but only one is provided
What it means
In buildPubsubRead, test hooks (a PubsubTestClientFactory and a Clock) must be supplied as a pair. If exactly one of clientFactory or clock is set on the provider, it throws, because the test client cannot operate correctly with only one of the two.
Solutions
- Set BOTH clientFactory and clock on the provider when testing.
- If not testing against a fake Pubsub client, clear BOTH fields so the production client path is used.
- Review test setup helpers to ensure the pair is always set together.
Example fix
// before provider = new PubsubReadSchemaTransformProvider().withClientFactory(factory); // clock missing // after provider = new PubsubReadSchemaTransformProvider().withClientFactory(factory).withClock(clock);
Defensive patterns
Strategy: validation
Validate before calling
if ((provider.clientFactory != null) != (provider.clock != null)) { throw new IllegalStateException("Set both PubsubTestClientFactory and Clock for testing, or neither"); } Try / catch
try { return provider.from(cfg); } catch (IllegalArgumentException e) { if (e.getMessage().contains("PubsubTestClientFactory")) { /* complete the test pair */ } throw e; } Prevention
- Encapsulate test setup in one helper that always sets clientFactory and clock together.
- Centralize fake-client construction so the pair cannot drift apart during refactors.
When it happens
Trigger: Constructing PubsubReadSchemaTransformProvider (e.g. via its builder) with only clientFactory set or only clock set, then calling from()/pubsubRead() to build the read transform — typically in unit tests.
Common situations: Test code that was updated to set the clock but forgot the client factory (or vice versa); refactoring that moved one of the two settings; partially copied test setup from another test class.
Related errors
- A schema was provided without a data format (or viceversa)…
- Cannot access key as parameter outside of @OnTimer method.
- Cannot access timerId as parameter outside of @OnTimer…
- Configuration schema provided does not match expected
- Dead letter queue topic name is not specified
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/bfa2ed2208a862c1.
Report an issue: GitHub.
Appendix: source
Thrown at sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/pubsub/PubsubReadSchemaTransformProvider.java:273
? PubsubIO.readMessages()
: PubsubIO.readMessagesWithAttributes();
String topic = configuration.getTopic();
if (topic != null && !topic.isEmpty()) {
pubsubRead = pubsubRead.fromTopic(topic);
} else {
String subscription = configuration.getSubscription();
pubsubRead =
pubsubRead.fromSubscription(
checkArgumentNotNull(
subscription, "Either topic or subscription must be specified"));
}
final PubsubTestClientFactory clientFactory = this.clientFactory;
final Clock clock = this.clock;
if (clientFactory != null && clock != null) {
pubsubRead = pubsubRead.withClientFactory(clientFactory);
pubsubRead = clientFactory.setClock(pubsubRead, clock);
} else if (clientFactory != null || clock != null) {
throw new IllegalArgumentException(
"Both PubsubTestClientFactory and Clock need to be specified for testing, but only one is provided");
}
String idAttribute = configuration.getIdAttribute();
if (idAttribute != null && !idAttribute.isEmpty()) {
pubsubRead = pubsubRead.withIdAttribute(idAttribute);
}
String timestampAttribute = configuration.getTimestampAttribute();
if (timestampAttribute != null && !timestampAttribute.isEmpty()) {
pubsubRead = pubsubRead.withTimestampAttribute(timestampAttribute);
}
return pubsubRead;
}
@Override
public PCollectionRowTuple expand(PCollectionRowTuple input) {
PubsubIO.Read<PubsubMessage> pubsubRead = buildPubsubRead();
PubsubReadSchemaTransformConfiguration.ErrorHandling errorHandling =
configuration.getErrorHandling();View on GitHub (pinned to 12126d8942)