{"record":{"id":"6d4e5160006e6e83","repo":"apache/beam","slug":"pubsub-topic-must-be-in-the-form-projects-project-topics","errorCode":null,"errorMessage":"PubSub topic must be in the form \"projects/<project>/topics/<topic>\" (got %r).","messagePattern":"PubSub topic must be in the form \"projects/<project>/topics/<topic>\" \\(got %r\\)\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"sdks/python/apache_beam/io/gcp/pubsub.py","lineNumber":489,"sourceCode":"        'with_attributes': DisplayDataItem(\n            True, label='With Attributes').drop_if_none(),\n        'timestamp_attribute': DisplayDataItem(\n            self.timestamp_attribute, label='Timestamp Attribute'),\n        'publish_with_ordering_key': DisplayDataItem(\n            self.publish_with_ordering_key,\n            label='Publish With Ordering Key').drop_if_none(),\n    }\n\n\nPROJECT_ID_REGEXP = '[a-z][-a-z0-9:.]{4,61}[a-z0-9]'\nSUBSCRIPTION_REGEXP = 'projects/([^/]+)/subscriptions/(.+)'\nTOPIC_REGEXP = 'projects/([^/]+)/topics/(.+)'\n\n\ndef parse_topic(full_topic: str) -> tuple[str, str]:\n  match = re.match(TOPIC_REGEXP, full_topic)\n  if not match:\n    raise ValueError(\n        'PubSub topic must be in the form \"projects/<project>/topics'\n        '/<topic>\" (got %r).' % full_topic)\n  project, topic_name = match.group(1), match.group(2)\n  if not re.match(PROJECT_ID_REGEXP, project):\n    raise ValueError('Invalid PubSub project name: %r.' % project)\n  return project, topic_name\n\n\ndef parse_subscription(full_subscription):\n  match = re.match(SUBSCRIPTION_REGEXP, full_subscription)\n  if not match:\n    raise ValueError(\n        'PubSub subscription must be in the form \"projects/<project>'\n        '/subscriptions/<subscription>\" (got %r).' % full_subscription)\n  project, subscription_name = match.group(1), match.group(2)\n  if not re.match(PROJECT_ID_REGEXP, project):\n    raise ValueError('Invalid PubSub project name: %r.' % project)\n  return project, subscription_name","sourceCodeStart":471,"sourceCodeEnd":507,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/python/apache_beam/io/gcp/pubsub.py#L471-L507","documentation":"parse_topic validates that a Pub/Sub topic string matches 'projects/([^/]+)/topics/(.+)' before constructing PubSub sinks/sources. A ValueError is raised when the string does not follow the fully-qualified projects/<project>/topics/<topic> form.","triggerScenarios":"Calling WriteToPubSub/ReadFromPubSub (or parse_topic directly) with a bare topic name like 'my-topic', a URL like 'pubsub.googleapis.com/...', or a truncated path.","commonSituations":"Users pasting only the topic id from the GCP console; forgetting the project prefix; using subscription-form strings where a topic is expected.","solutions":["Pass the fully-qualified form: 'projects/my-project/topics/my-topic'","Format the string programmatically from project and topic variables: f'projects/{project}/topics/{topic}'","Check for stray whitespace or a leading/trailing slash that breaks the regexp match"],"exampleFix":"// before\nWriteToPubSub(topic='my-topic')\n// after\nWriteToPubSub(topic='projects/my-gcp-project/topics/my-topic')","handlingStrategy":"validation","validationCode":"import re\nassert re.match(r'projects/([^/]+)/topics/(.+)', topic), f'bad topic: {topic!r}'","typeGuard":null,"tryCatchPattern":"try:\n    pcoll | beam.io.WriteToPubSub(topic=topic)\nexcept ValueError as e:\n    if 'PubSub topic must be in the form' in str(e):\n        topic = f'projects/{PROJECT}/topics/{topic_name}'\n        pcoll | beam.io.WriteToPubSub(topic=topic)\n    else:\n        raise","preventionTips":["Store fully-qualified topic paths in config, never bare topic ids","Build topic strings with f'projects/{p}/topics/{t}'","Trim whitespace from config values before use"],"tags":["python","pubsub","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-14T16:17:12.679Z"}