{"record":{"id":"62fee679ed4e405e","repo":"apache/beam","slug":"external-environment-endpoint-must-be-set","errorCode":null,"errorMessage":"External environment endpoint must be set.","messagePattern":"External environment endpoint must be set\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"sdks/python/apache_beam/transforms/environments.py","lineNumber":575,"sourceCode":"      resource_hints,  # type: Mapping[str, bytes]\n      context  # type: PipelineContext\n  ):\n    # type: (...) -> ExternalEnvironment\n    return ExternalEnvironment(\n        payload.endpoint.url,\n        params=payload.params or None,\n        capabilities=capabilities,\n        artifacts=artifacts,\n        resource_hints=resource_hints)\n\n  @classmethod\n  def from_options(cls, options):\n    # type: (PortableOptions) -> ExternalEnvironment\n    if looks_like_json(options.environment_config):\n      config = json.loads(options.environment_config)\n      url = config.get('url')\n      if not url:\n        raise ValueError('External environment endpoint must be set.')\n      params = config.get('params')\n    elif options.environment_config:\n      url = options.environment_config\n      params = None\n    else:\n      url = options.lookup_environment_option('external_service_address')\n      params = None\n\n    return cls(\n        url,\n        params=params,\n        capabilities=python_sdk_capabilities(),\n        artifacts=python_sdk_dependencies(options),\n        resource_hints=resource_hints_from_options(options))\n\n\ndef expand_anyof_environments(env_proto):\n  if env_proto.urn == common_urns.environments.ANYOF.urn:","sourceCodeStart":557,"sourceCodeEnd":593,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/python/apache_beam/transforms/environments.py#L557-L593","documentation":"ExternalEnvironment.from_options parses environment_config as JSON and requires a 'url' key giving the external environment's gRPC endpoint; if the JSON parses but has no url (or the value is empty), it raises ValueError 'External environment endpoint must be set.'","triggerScenarios":"--environment_config='{\"params\": {...}}' (JSON without 'url'), or environment_config set to '{\"url\": null}'. The non-JSON / fallback branches (raw endpoint string or lookup_environment_option) are unaffected.","commonSituations":"Hand-written JSON config omitting the url key, config templated from another environment type (e.g. process config pasted as external), or url intentionally left blank expecting a default that doesn't exist.","solutions":["Add the endpoint to the JSON: {\"url\": \"host:port\"}.","Or pass the endpoint directly as environment_config='host:port' instead of JSON.","Or set the external_service_address pipeline option so the fallback lookup finds it."],"exampleFix":"// before\n--environment_config='{\"params\": {\"docker_image\": \"x\"}}'\n// after\n--environment_config='{\"url\": \"localhost:50051\", \"params\": {}}'","handlingStrategy":"validation","validationCode":"import json\ndef has_url(cfg):\n    try:\n        return bool(json.loads(cfg).get('url'))\n    except json.JSONDecodeError:\n        return True  # raw endpoint string is fine","typeGuard":null,"tryCatchPattern":"try:\n    env = ExternalEnvironment.from_options(options)\nexcept ValueError as e:\n    if 'endpoint must be set' in str(e): ...","preventionTips":["Always include \"url\" in external environment JSON configs.","Prefer passing the endpoint as the plain environment_config string.","Validate JSON config with a lint step before launching pipelines."],"tags":["beam","environment","external-service","missing-field","config"],"backgroundTag":"missing-required-config-field","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"}