apple/pkl · error

cannotGlobTripleDots

cannotGlobTripleDots

Error message

cannotGlobTripleDots

What it means

Thrown when a globbed read (or import) pattern starts with `...` (triple dots). Relative paths beginning with `...` denote parent-directory traversal in Pkl's module scheme, and globbing across such paths is deliberately unsupported. The check mirrors the one used for globbed imports in AstBuilder.

Solutions

  1. Remove the leading `...` and glob within the current module's directory instead.
  2. Place the evaluation entry point (root module) in the common parent directory so patterns like `**/*.pkl` cover the files.
  3. Reference the parent files via explicit imports rather than globbing.
  4. Use a project (PklProject) so the target files are within the project root.

Example fix

// before
read*("../*.pkl")  // or ".../*.pkl"
// after
read*("**/*.pkl")  // run from the common parent directory
Defensive patterns

Strategy: validation

Validate before calling

function assertNoTripleDots(pattern) {
  if (String(pattern).startsWith("...")) throw new Error("cannot glob '...' paths")
  return pattern
}

Type guard

const isGlobbablePattern = (p) => typeof p === 'string' && !p.startsWith('...')

Prevention

When it happens

Trigger: Calling `read*(".../*.pkl")` or `import*(".../*.x")` — a glob pattern whose string form begins with `...` — in ReadGlobNode.read.

Common situations: Trying to glob files in parent directories of the current module, porting shell glob habits where `..` traversal plus wildcards seems natural.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


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

Appendix: source

Thrown at pkl-core/src/main/java/org/pkl/core/ast/expression/unary/ReadGlobNode.java:75

              sourceSection,
              "",
              language,
              new FrameDescriptor(),
              new ReadGlobMemberBodyNode(sourceSection));
    }
    return memberNode;
  }

  @Specialization
  @TruffleBoundary
  public Object read(String globPattern) {
    var cachedResult = cachedResults.get(globPattern);
    //noinspection ConstantValue
    if (cachedResult != null) return cachedResult;

    // use same check as for globbed imports (see AstBuilder)
    if (globPattern.startsWith("...")) {
      throw exceptionBuilder().evalError("cannotGlobTripleDots").build();
    }
    var globUri = parseUri(globPattern);
    var context = VmContext.get(this);
    try {
      var resolvedUri = IoUtils.resolve(context.getSecurityManager(), currentModule, globUri);
      var reader = context.getResourceManager().getReader(resolvedUri, this);
      if (!reader.isGlobbable()) {
        throw exceptionBuilder().evalError("cannotGlobUri", globUri, globUri.getScheme()).build();
      }
      var resolvedElements =
          GlobResolver.resolveGlob(
              context.getSecurityManager(),
              reader,
              currentModule,
              currentModule.getUri(),
              globPattern);
      var builder = new VmObjectBuilder(resolvedElements.size());
      for (var entry : resolvedElements.entrySet()) {

View on GitHub (pinned to f3efcbfc9b)