{"record":{"id":"9bfaae80c1ec8a5e","repo":"apache/beam","slug":"exactly-one-of-topic-or-subscription-must-be-set-in","errorCode":null,"errorMessage":"Exactly one of Topic or Subscription must be set in ReadOptions","messagePattern":"Exactly one of Topic or Subscription must be set in ReadOptions","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"sdks/go/pkg/beam/io/pubsubio/pubsubio.go","lineNumber":72,"sourceCode":"type ReadOptions struct {\n\tTopic              string // Topic sets the topic to be read from. A new subscription will be generated for the job. Mutually exclusive with setting a Subscription.\n\tSubscription       string // Subscription sets the name of an existing subscription to read from. Mutually exclusive with setting a Topic.\n\tIDAttribute        string\n\tTimestampAttribute string\n\tWithAttributes     bool\n}\n\n// Read reads an unbounded number of PubSubMessages from the given\n// pubsub topic or subscription. It produces an unbounded PCollecton<*PubSubMessage>,\n// if WithAttributes is set, or an unbounded PCollection<[]byte>.\n//\n// The topic or subscription is required and must be set with ReadOptions.\nfunc Read(s beam.Scope, project string, opts ReadOptions) beam.PCollection {\n\ts = s.Scope(\"pubsubio.Read\")\n\n\t// Validate: only one of Topic or Subscription should be set\n\tif (opts.Topic == \"\" && opts.Subscription == \"\") || (opts.Topic != \"\" && opts.Subscription != \"\") {\n\t\tpanic(\"Exactly one of Topic or Subscription must be set in ReadOptions\")\n\t}\n\n\tpayload := &pipepb.PubSubReadPayload{}\n\n\tif opts.Topic != \"\" {\n\t\tpayload.Topic = pubsubx.MakeQualifiedTopicName(project, opts.Topic)\n\t} else {\n\t\tpayload.Subscription = pubsubx.MakeQualifiedSubscriptionName(project, opts.Subscription)\n\t}\n\n\tpayload.IdAttribute = opts.IDAttribute\n\tpayload.TimestampAttribute = opts.TimestampAttribute\n\tpayload.WithAttributes = opts.WithAttributes\n\n\tout := beam.External(\n\t\ts,\n\t\treadURN,\n\t\tprotox.MustEncode(payload),","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/go/pkg/beam/io/pubsubio/pubsubio.go#L54-L90","documentation":"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.","triggerScenarios":"Calling pubsubio.Read(s, project, pubsubio.ReadOptions{}) with both Topic and Subscription populated, or with both left empty.","commonSituations":"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.","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."],"exampleFix":"// before\npubsubio.Read(s, project, pubsubio.ReadOptions{Topic: \"t\", Subscription: \"sub\"})\n// after\npubsubio.Read(s, project, pubsubio.ReadOptions{Subscription: \"sub\"})","handlingStrategy":"validation","validationCode":"func validateReadOptions(o pubsubio.ReadOptions) error {\n    if (o.Topic == \"\") == (o.Subscription == \"\") {\n        return errors.New(\"set exactly one of Topic or Subscription\")\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"defer func() {\n    if r := recover(); r != nil {\n        if s, ok := r.(string); ok && strings.Contains(s, \"Exactly one of Topic or Subscription\") {\n            // handle\n        } else { panic(r) }\n    }\n}()","preventionTips":["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."],"tags":["go","apache-beam","pubsub","panic","mutually-exclusive-options"],"backgroundTag":"mutually-exclusive-options","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-20T03:17:13.778Z"}