{"record":{"id":"0c1d77a7a33b02f2","repo":"apache/beam","slug":"we-currently-only-support-wildcards-at-terminal-parts-of","errorCode":null,"errorMessage":"We currently only support wildcards at terminal parts of selectors. 'x.*' is allowed, but x.*.y is not currently allowed.","messagePattern":"We currently only support wildcards at terminal parts of selectors\\. 'x\\.\\*' is allowed, but x\\.\\*\\.y is not currently allowed\\.","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/parser/FieldAccessDescriptorParser.java","lineNumber":83,"sourceCode":"    @Override\n    public FieldAccessDescriptor visitFieldSpecifier(FieldSpecifierContext ctx) {\n      return ctx.dotExpression().accept(this);\n    }\n\n    @Override\n    public FieldAccessDescriptor visitDotExpression(DotExpressionContext ctx) {\n      List<FieldAccessDescriptor> components =\n          ctx.dotExpressionComponent().stream()\n              .map(dotE -> dotE.accept(this))\n              .collect(Collectors.toList());\n\n      // Walk backwards through the list to build up the nested FieldAccessDescriptor.\n      checkArgument(!components.isEmpty());\n      FieldAccessDescriptor fieldAccessDescriptor = components.get(components.size() - 1);\n      for (int i = components.size() - 2; i >= 0; --i) {\n        FieldAccessDescriptor component = components.get(i);\n        if (component.getAllFields()) {\n          throw new IllegalArgumentException(\n              \"We currently only support wildcards at terminal\"\n                  + \" parts of selectors. 'x.*' is allowed, but x.*.y is not currently allowed.\");\n          // TODO: We should support expanding out x.*.y expressions.\n        }\n        FieldDescriptor fieldAccessed =\n            component.getFieldsAccessed().stream()\n                .findFirst()\n                .orElseThrow(IllegalArgumentException::new);\n\n        fieldAccessDescriptor =\n            FieldAccessDescriptor.withFields()\n                .withNestedField(fieldAccessed, fieldAccessDescriptor);\n      }\n      return fieldAccessDescriptor;\n    }\n\n    @Override\n    public FieldAccessDescriptor visitQualifyComponent(QualifyComponentContext ctx) {","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/java/core/src/main/java/org/apache/beam/sdk/schemas/parser/FieldAccessDescriptorParser.java#L65-L101","documentation":"FieldAccessDescriptorParser builds a nested FieldAccessDescriptor from a selector string like 'x.*.y'. It walks the parsed components backwards, and if it finds a wildcard ('*') component that is not the terminal (last) component, it throws because nested wildcard expansion is not implemented.","triggerScenarios":"Parsing a field selector string via FieldAccessDescriptorParser.parseFrom where a '*' appears in a non-final position, e.g. 'x.*.y' or 'x.*.z.w'.","commonSituations":"Users of Beam SQL/schema transforms write selector strings assuming glob-like semantics where '*' can appear anywhere, similar to JSONPath or Spark column expansion; the TODO in the source confirms this is an unimplemented feature, not a user error per se.","solutions":["Restructure the selector so the wildcard is terminal: 'x.*' instead of 'x.*.y'.","Expand the wildcard manually: enumerate the actual field names of the nested schema and write them out explicitly, e.g. 'x.a.b', 'x.c.b'.","Use FieldAccessDescriptor.builder() programmatically with explicit field names instead of the string parser.","Check the Beam version/issue tracker: support for 'x.*.y' is a noted TODO and may land in later releases."],"exampleFix":"// before\nFieldAccessDescriptor desc = FieldAccessDescriptorParser.parseFrom(\"x.*.y\");\n// after\nFieldAccessDescriptor desc = FieldAccessDescriptorParser.parseFrom(\"x.*\");\n// or explicitly:\nFieldAccessDescriptor desc = FieldAccessDescriptor.withFieldNames(\"x.a.b\", \"x.c.b\");","handlingStrategy":"validation","validationCode":"static boolean hasNonTerminalWildcard(String selector) {\n  String[] parts = selector.split(\"\\\\.\");\n  for (int i = 0; i < parts.length - 1; i++) {\n    if (\"*\".equals(parts[i])) return true;\n  }\n  return false;\n}\nif (hasNonTerminalWildcard(selector)) throw new IllegalArgumentException(\"wildcard must be terminal\");","typeGuard":"boolean isSafeSelector(String s) { return s != null && !hasNonTerminalWildcard(s); }","tryCatchPattern":"try { desc = FieldAccessDescriptorParser.parseFrom(selector); } catch (IllegalArgumentException e) { if (e.getMessage().contains(\"wildcards\")) { /* fall back to explicit field list */ } else throw e; }","preventionTips":["Keep '*' as the last component of any selector","Enumerate nested schema field names explicitly instead of using mid-path wildcards","Unit-test selector strings against FieldAccessDescriptorParser before pipeline launch"],"tags":["java","apache-beam","schemas","selector-parsing"],"backgroundTag":"unsupported-operation","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-14T21:17:11.552Z"}