apache/beam · error · IllegalStateException
Can't set both the topic and the subscription for a PubsubIO
Error message
Can't set both the topic and the subscription for a PubsubIO.Read transform
What it means
Read.expand() enforces mutually exclusive sources: a PubsubIO.Read transform may read from a topic OR a subscription, never both. When both are set, this IllegalStateException is thrown at pipeline construction.
Source
Thrown at sdks/java/io/google-cloud-platform/src/main/java/org/apache/beam/sdk/io/gcp/pubsub/PubsubIO.java:1228
@VisibleForTesting
/**
* Set's the internal Clock.
*
* <p>Only for use by unit tests.
*/
Read<T> withClock(Clock clock) {
return toBuilder().setClock(clock).build();
}
@Override
public PCollection<T> expand(PBegin input) {
if (getTopicProvider() == null && getSubscriptionProvider() == null) {
throw new IllegalStateException(
"Need to set either the topic or the subscription for " + "a PubsubIO.Read transform");
}
if (getTopicProvider() != null && getSubscriptionProvider() != null) {
throw new IllegalStateException(
"Can't set both the topic and the subscription for " + "a PubsubIO.Read transform");
}
if (getDeadLetterTopicProvider() != null
&& !(getBadRecordRouter() instanceof ThrowingBadRecordRouter)) {
throw new IllegalArgumentException(
"PubSubIO cannot be configured with both a dead letter topic and a bad record router");
}
ValueProvider<PubsubTopic> topicProvider = getTopicProvider();
@Nullable ValueProvider<TopicPath> topicPath =
topicProvider == null
? null
: NestedValueProvider.of(topicProvider, new TopicPathTranslator());
ValueProvider<PubsubSubscription> subscriptionProvider = getSubscriptionProvider();
@Nullable ValueProvider<SubscriptionPath> subscriptionPath =
subscriptionProvider == null
? nullView on GitHub (pinned to 12126d8942)
Solutions
- Set only one: keep fromSubscription(...) if you want a durable subscription, and remove the fromTopic(...) call.
- If both values exist in config, choose with an if/else instead of setting both.
- Note topic-vs-subscription semantics differ (subscription tracks acks); pick deliberately.
Example fix
// before PubsubIO.readStrings().fromTopic(t).fromSubscription(s); // after PubsubIO.readStrings().fromSubscription(s); // or fromTopic(t), never both
Defensive patterns
Strategy: validation
Validate before calling
checkArgument(topic == null || subscription == null,
"Set only one of topic or subscription, not both");
PubsubIO.Read<String> read = subscription != null
? PubsubIO.readStrings().fromSubscription(subscription)
: PubsubIO.readStrings().fromTopic(topic); Type guard
boolean exactlyOneSource(String topic, String subscription) {
return (topic != null) ^ (subscription != null);
} Try / catch
try {
return pipeline.apply(read);
} catch (IllegalStateException e) {
if (e.getMessage().contains("Can't set both the topic and the subscription"))
throw new ConfigException("Provide either topic or subscription, not both", e);
throw e;
} Prevention
- Use if/else (not both) when config carries both keys
- Wrap source selection in a single factory method
- Validate exactly-one-of constraints before building the pipeline
When it happens
Trigger: Calling both fromTopic(...) and fromSubscription(...) on the same Read transform — commonly when merging config values or chaining builder calls where each branch sets a different source.
Common situations: Config providing both topic and subscription keys and code setting both unconditionally; combining a default builder (with a topic) with a subscription override.
Related errors
- Need to set either the topic or the subscription for a Pubsu
- To read from Pubsub, a subscription name or a topic name mus
- Unrecognized value for stable unique names:
- Secret option string cannot be null
- Secret string must contain a valid type parameter
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/524886962c5f3ff3.
Report an issue: GitHub.