apache/beam · error
Exactly one of Topic or Subscription must be set in…
Error message
Exactly one of Topic or Subscription must be set in ReadOptions
What it means
pubsubio.Read requires ReadOptions to set exactly one of Topic or Subscription. The library panics when both are set (ambiguous source) or neither is set (no source). Validation happens synchronously during pipeline construction.
Solutions
- Set exactly one field: use Topic to read from a topic, or Subscription for an existing subscription.
- If both are populated from config, add logic to prefer/clear one before calling Read.
- Guard with a validation check on the options struct before constructing the pipeline.
Example fix
// before
pubsubio.Read(s, project, pubsubio.ReadOptions{Topic: "t", Subscription: "sub"})
// after
pubsubio.Read(s, project, pubsubio.ReadOptions{Subscription: "sub"}) Defensive patterns
Strategy: validation
Validate before calling
func validateReadOptions(o pubsubio.ReadOptions) error {
if (o.Topic == "") == (o.Subscription == "") {
return errors.New("set exactly one of Topic or Subscription")
}
return nil
} Try / catch
defer func() {
if r := recover(); r != nil {
if s, ok := r.(string); ok && strings.Contains(s, "Exactly one of Topic or Subscription") {
// handle
} else { panic(r) }
}
}() Prevention
- Validate the options struct before calling pubsubio.Read.
- When sourcing from flags/config, ensure only one of the two fields is wired.
- Prefer Subscription for production reads to preserve acknowledgements.
When it happens
Trigger: Calling pubsubio.Read(s, project, pubsubio.ReadOptions{}) with both Topic and Subscription populated, or with both left empty.
Common situations: Copy-pasting ReadOptions between read and write code leaving stale fields; building options from flags where one flag was renamed and never wired; templated pipelines that fill both fields unconditionally.
Related errors
- pubsubio.Write only accepts PCollections of
- AfterProcessingTime trigger set without a delay or…
- At least one subtrigger required for composite triggers.
- attempted to add namespace to missing coder id
- attempted to add namespace to missing windowing strategy id
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/9bfaae80c1ec8a5e.
Report an issue: GitHub.
Appendix: source
Thrown at sdks/go/pkg/beam/io/pubsubio/pubsubio.go:72
type ReadOptions struct {
Topic string // Topic sets the topic to be read from. A new subscription will be generated for the job. Mutually exclusive with setting a Subscription.
Subscription string // Subscription sets the name of an existing subscription to read from. Mutually exclusive with setting a Topic.
IDAttribute string
TimestampAttribute string
WithAttributes bool
}
// Read reads an unbounded number of PubSubMessages from the given
// pubsub topic or subscription. It produces an unbounded PCollecton<*PubSubMessage>,
// if WithAttributes is set, or an unbounded PCollection<[]byte>.
//
// The topic or subscription is required and must be set with ReadOptions.
func Read(s beam.Scope, project string, opts ReadOptions) beam.PCollection {
s = s.Scope("pubsubio.Read")
// Validate: only one of Topic or Subscription should be set
if (opts.Topic == "" && opts.Subscription == "") || (opts.Topic != "" && opts.Subscription != "") {
panic("Exactly one of Topic or Subscription must be set in ReadOptions")
}
payload := &pipepb.PubSubReadPayload{}
if opts.Topic != "" {
payload.Topic = pubsubx.MakeQualifiedTopicName(project, opts.Topic)
} else {
payload.Subscription = pubsubx.MakeQualifiedSubscriptionName(project, opts.Subscription)
}
payload.IdAttribute = opts.IDAttribute
payload.TimestampAttribute = opts.TimestampAttribute
payload.WithAttributes = opts.WithAttributes
out := beam.External(
s,
readURN,
protox.MustEncode(payload),View on GitHub (pinned to 12126d8942)