{"record":{"id":"46207e9617d943cb","repo":"apache/beam","slug":"unexpected-expansion-service-address-expected-to-be-in-the","errorCode":null,"errorMessage":"Unexpected expansion service address. Expected to be in the format \"<host>:<port>\"","messagePattern":"Unexpected expansion service address\\. Expected to be in the format \"<host>:<port>\"","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"sdks/java/extensions/python/src/main/java/org/apache/beam/sdk/extensions/python/PythonExternalTransform.java","lineNumber":474,"sourceCode":"  private boolean isDockerAvailable() {\n    String executable = \"docker\";\n    try {\n      new ProcessBuilder(executable, \"--version\").start().waitFor();\n      return true;\n    } catch (IOException | InterruptedException exn) {\n      // Ignore.\n    }\n    return false;\n  }\n\n  @Override\n  public OutputT expand(InputT input) {\n    try {\n      ExternalTransforms.ExternalConfigurationPayload payload = generatePayload();\n      if (!Strings.isNullOrEmpty(expansionService)) {\n        int portIndex = expansionService.lastIndexOf(':');\n        if (portIndex <= 0) {\n          throw new IllegalArgumentException(\n              \"Unexpected expansion service address. Expected to be in the \"\n                  + \"format \\\"<host>:<port>\\\"\");\n        }\n        PythonService.waitForPort(\n            expansionService.substring(0, portIndex),\n            Integer.parseInt(expansionService.substring(portIndex + 1, expansionService.length())),\n            15000);\n        return apply(input, expansionService, payload);\n      } else {\n        OutputT output = null;\n        int port = PythonService.findAvailablePort();\n        PipelineOptionsFactory.register(PythonExternalTransformOptions.class);\n        PythonExternalTransformOptions options =\n            input.getPipeline().getOptions().as(PythonExternalTransformOptions.class);\n        boolean useTransformService = options.getUseTransformService();\n        @Nullable String customBeamRequirement = options.getCustomBeamRequirement();\n        boolean pythonAvailable = isPythonAvailable();\n        boolean dockerAvailable = isDockerAvailable();","sourceCodeStart":456,"sourceCodeEnd":492,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/java/extensions/python/src/main/java/org/apache/beam/sdk/extensions/python/PythonExternalTransform.java#L456-L492","documentation":"When an expansion service address is given, PythonExternalTransform.expand parses it as \"<host>:<port>\" so it can wait for the service port before expansion. The library throws IllegalArgumentException if the address has no colon or the colon is the first character, meaning it cannot extract a valid host/port pair.","triggerScenarios":"Setting expansionService via withExpansionService or the constructor to a value like \"localhost\" (no port), \":5000\" (empty host, portIndex == 0), or any string whose lastIndexOf(':') is <= 0.","commonSituations":"Copy-pasting a service URL without the port; using a URI like \"sc://host\" scheme syntax; forgetting the default port; typos in the address.","solutions":["Pass the full address including port, e.g. \"localhost:5000\"","If relying on the default expansion service, do not set expansionService at all (pass null/empty) so the address check is skipped","Validate the address format with a regex before calling withExpansionService"],"exampleFix":"// before\ntransform.withExpansionService(\"localhost\"); // throws\n// after\ntransform.withExpansionService(\"localhost:5000\");","handlingStrategy":"validation","validationCode":"if (expansionService != null && !expansionService.isEmpty()) {\n  int i = expansionService.lastIndexOf(':');\n  if (i <= 0) throw new IllegalArgumentException(\"expansionService must be <host>:<port>\");\n  Integer.parseInt(expansionService.substring(i + 1));\n}\ntransform.withExpansionService(expansionService);","typeGuard":"boolean isValidServiceAddr(String s) {\n  if (s == null || s.isEmpty()) return true; // default service\n  int i = s.lastIndexOf(':');\n  return i > 0 && i < s.length() - 1;\n}","tryCatchPattern":"try {\n  transform.withExpansionService(addr);\n} catch (IllegalArgumentException e) {\n  // fall back to default expansion service or fix address\n}","preventionTips":["Always pass host:port form, e.g. localhost:5000","Leave expansionService unset to use the default service","Validate with a regex like ^[^:]+:\\\\d+$ before configuring"],"tags":["java","beam","address-format","expansion-service","illegal-argument"],"backgroundTag":"invalid-url-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"}