{"record":{"id":"357698e1610a40c5","repo":"apache/beam","slug":"pubsub-source-descriptor-must-be-in-the-form-projects","errorCode":null,"errorMessage":"PubSub source descriptor must be in the form \"projects/<project>/topics/<topic>\" or \"projects/<project>/subscription/<subscription>\" (got %r).","messagePattern":"PubSub source descriptor must be in the form \"projects/<project>/topics/<topic>\" or \"projects/<project>/subscription/<subscription>\" \\(got %r\\)\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"sdks/python/apache_beam/io/gcp/pubsub.py","lineNumber":798,"sourceCode":"      with_attributes: bool = False,\n  ):\n    \"\"\"Initializes ``PubSubMultipleReader``.\n\n    Args:\n      pubsub_source_descriptors: List of Cloud Pub/Sub topics or subscriptions\n        of type `~PubSubSourceDescriptor`.\n      with_attributes:\n        True - input elements will be :class:`~PubsubMessage` objects.\n        False - input elements will be of type ``bytes`` (message data only).\n    \"\"\"\n    self.pubsub_source_descriptors = pubsub_source_descriptors\n    self.with_attributes = with_attributes\n\n    for descriptor in self.pubsub_source_descriptors:\n      match_descriptor = re.match(PUBSUB_DESCRIPTOR_REGEXP, descriptor.source)\n\n      if not match_descriptor:\n        raise ValueError(\n            'PubSub source descriptor must be in the form \"projects/<project>'\n            '/topics/<topic>\" or \"projects/<project>/subscription'\n            '/<subscription>\" (got %r).' % descriptor.source)\n\n  def expand(self, pcol):\n    sources_pcol = []\n    for descriptor in self.pubsub_source_descriptors:\n      source_match = re.match(PUBSUB_DESCRIPTOR_REGEXP, descriptor.source)\n      source_project = source_match.group(1)\n      source_type = source_match.group(2)\n      source_name = source_match.group(3)\n\n      read_step_name = 'PubSub %s/project:%s/Read %s' % (\n          source_type, source_project, source_name)\n\n      if source_type == 'topics':\n        current_source = pcol | read_step_name >> ReadFromPubSub(\n            topic=descriptor.source,","sourceCodeStart":780,"sourceCodeEnd":816,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/python/apache_beam/io/gcp/pubsub.py#L780-L816","documentation":"apache_beam raises this ValueError in _WriteToPubSub.__init__ when a PubSub source descriptor string does not match the required pattern projects/<project>/topics/<topic> or projects/<project>/subscriptions/<subscription>. Beam validates the descriptor with PUBSUB_DESCRIPTOR_REGEXP before building the pipeline so malformed topic/subscription paths fail fast at construction time rather than at runtime on the runner.","triggerScenarios":"Passing a string like 'my-topic', 'projects/p/topics/', or a full https://pubsub.googleapis.com/... URL as the topic/subscription argument to WriteToPubSub / ReadFromPubSub (or constructing ReadFromPubSub with an invalid source).","commonSituations":"Users pass just the topic name without the projects/<project>/topics/ prefix; copy-pasted full resource URLs from the Cloud Console; typos in 'topics'/'subscriptions' (singular vs plural); project IDs containing characters outside the allowed pattern.","solutions":["Prefix the topic with 'projects/<your-project>/topics/' (e.g. 'projects/my-proj/topics/my-topic').","For subscriptions use 'projects/<your-project>/subscriptions/<sub>' (note the plural 'subscriptions').","Strip any scheme/host from console URLs and keep only the projects/... resource path.","Verify the string matches ^projects/[^/]+/(topics|subscriptions)/[^/]+$ before constructing the transform."],"exampleFix":"// before\nbeam.io.WriteToPubSub('my-topic')\n// after\nbeam.io.WriteToPubSub('projects/my-gcp-project/topics/my-topic')","handlingStrategy":"validation","validationCode":"import re\nPUBSUB_DESCRIPTOR_REGEXP = re.compile(r'projects/[a-zA-Z0-9-_.~]+/(topics|subscriptions)/[a-zA-Z0-9-_.~%+]+')\ndef is_valid_descriptor(d: str) -> bool:\n    return bool(PUBSUB_DESCRIPTOR_REGEXP.match(d))\nassert is_valid_descriptor('projects/my-proj/topics/my-topic')","typeGuard":"def is_pubsub_descriptor(x: object) -> bool:\n    return isinstance(x, str) and bool(PUBSUB_DESCRIPTOR_REGEXP.match(x))","tryCatchPattern":"try:\n    beam.io.WriteToPubSub(topic)\nexcept ValueError as e:\n    if 'PubSub source descriptor' in str(e):\n        topic = f'projects/{project}/topics/{topic}'\n    else:\n        raise","preventionTips":["Always build descriptors as projects/<project>/topics/<name> or projects/<project>/subscriptions/<name>","Never paste full console URLs; keep only the resource path","Remember 'subscriptions' is plural","Unit-test the descriptor strings before pipeline construction"],"tags":["google-cloud-pubsub","python","validation","apache-beam"],"backgroundTag":"invalid-argument-format","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"}