apple/pkl · error · InvalidGlobPatternException

invalidGlobExtGlob

invalidGlobExtGlob

Error message

invalidGlobExtGlob

What it means

GlobResolver rejects extended-glob syntax: a `?` immediately followed by `(` would begin an extglob alternation like ?(a|b), which this glob implementation does not support. A plain `?` matches exactly one character, so `?(` is rejected rather than misinterpreted.

Solutions

  1. Rewrite the extglob alternation as a brace or character-class expansion, e.g. "?(main|test).pkl" -> "{main,test}.pkl"
  2. Escape or restructure so `(` is not immediately after `?` — a literal `(` should appear as `(` alone (it is escaped by the translator) not right after a wildcard
  3. Use separate globs for each alternative

Example fix

// before
glob("?(a|b).pkl")
// after
glob("{a,b}.pkl")
Defensive patterns

Strategy: validation

Validate before calling

/\?\(/.test(glob) // true means unsupported extglob ?( — rewrite before use

Type guard

const supportsPlainGlobOnly = (p) => !/\?\(/.test(p);

Try / catch

try {
  var regex = GlobResolver.toRegexPattern(glob);
} catch (InvalidGlobPatternException e) {
  if (e.getMessage().contains("extglob")) throw new IllegalArgumentException("extglob ?( syntax unsupported; use {a,b}");
  throw e;
}

Prevention

When it happens

Trigger: Calling toRegexPattern/toRegexString (via glob import or resource glob resolution) with a pattern where `?` is directly followed by `(`, e.g. "?(main|test).pkl".

Common situations: Copying patterns from bash with extglob enabled (shopt -s extglob) or from tools like minimatch/jq that support extglob syntax into a Pkl glob.

Related errors


AI-assisted analysis of apple/pkl@f3efcbfc9b (2026-09-08). Data as JSON: /api/errors/3526fc5096155dde. Report an issue: GitHub.

Appendix: source

Thrown at pkl-core/src/main/java/org/pkl/core/util/GlobResolver.java:199

        }
        case '\\' -> {
          var next = getNextChar(globPattern, i);
          if (next == NULL) {
            throw new InvalidGlobPatternException(
                ErrorMessages.create("invalidGlobInvalidTerminatingCharacter"));
          }
          if (next != '?' && next != '*' && next != '[' && next != '{' && next != '\\') {
            throw new InvalidGlobPatternException(
                ErrorMessages.create("invalidGlobInvalidEscapeCharacter", next));
          }
          sb.append('\\').append(next);
          i++;
        }
        case '[' -> i = consumeCharacterClass(globPattern, i, sb);
        case '?' -> {
          var next = getNextChar(globPattern, i);
          if (next == '(') {
            throw new InvalidGlobPatternException(ErrorMessages.create("invalidGlobExtGlob"));
          }
          sb.append(".");
        }
        case '*' -> {
          var next = getNextChar(globPattern, i);
          if (next == '(') {
            throw new InvalidGlobPatternException(ErrorMessages.create("invalidGlobExtGlob"));
          } else if (next == '*') {
            // globstar, crosses directory boundaries
            sb.append(".*");
            i++;
          } else {
            // single wildcard matches everything up until the next directory character
            sb.append("[^/]*");
          }
        }
        case '+', '@' -> {
          var next = getNextChar(globPattern, i);

View on GitHub (pinned to f3efcbfc9b)