apple/pkl · error · VmException

ioErrorReadingTestOutputFile

ioErrorReadingTestOutputFile

Error message

ioErrorReadingTestOutputFile

What it means

`TestRunner.loadExampleOutputs` reads an expected or actual output Pcf file from disk to compare against fresh results. An IOException from `Files.readString` is wrapped as `ioErrorReadingTestOutputFile` naming the file. Since the file's existence is normally checked just before, this usually means a race, permissions problem, or invalid-encoding/IO issue.

Source

Thrown at pkl-core/src/main/java/org/pkl/core/runtime/TestRunner.java:373

    var builder = new StringBuilder();
    new PcfRenderer(builder, "  ", converter, false, true).renderDocument(outputFileContent);
    try {
      Files.writeString(outputFile, builder);
    } catch (IOException e) {
      throw new VmExceptionBuilder()
          .evalError("ioErrorWritingTestOutputFile", outputFile)
          .withCause(e)
          .build();
    }
  }

  private VmDynamic loadExampleOutputs(Path outputFile) {
    // load file manually to prevent it from being cached (won't need it again)
    String fileContent;
    try {
      fileContent = Files.readString(outputFile, StandardCharsets.UTF_8);
    } catch (IOException e) {
      throw new VmExceptionBuilder()
          .evalError("ioErrorReadingTestOutputFile", outputFile)
          .withCause(e)
          .build();
    }
    var module =
        VmLanguage.get(null).loadModule(ModuleKeys.synthetic(outputFile.toUri(), fileContent));
    var examples = (VmDynamic) VmUtils.readMemberOrNull(module, Identifier.EXAMPLES);
    if (examples == null) {
      throw new VmExceptionBuilder().evalError("invalidOutputFileStructure", outputFile).build();
    }
    return examples;
  }

  private static String renderAsPcf(Object pklValue) {
    var builder = new StringBuilder();
    new PcfRenderer(builder, "  ", converter, false, false).renderValue(pklValue);
    if (pklValue instanceof VmObject) {
      builder.insert(0, "new ");

View on GitHub (pinned to f3efcbfc9b)

Solutions

  1. Verify the output file still exists and is readable (ls -l, check ownership/permissions).
  2. Stop concurrent processes that delete or regenerate `*-pcf` files during the run.
  3. Regenerate the expected file with `pkl test --overwrite` (after ensuring write access).
  4. Avoid running tests against files on flaky network mounts; use a local copy.

Example fix

// before: expected file owned by root, test run as non-root
// after
sudo chown "$(whoami)" MyTest-expected.pcf && chmod u+rw MyTest-expected.pcf
pkl test MyTest.pkl
Defensive patterns

Strategy: validation

Validate before calling

// verify output files are readable before testing
Path expected = moduleFile.resolveSibling(moduleFile.getFileName() + "-expected.pcf");
if (Files.exists(expected) && !Files.isReadable(expected)) {
    throw new IllegalStateException("Unreadable: " + expected);
}

Try / catch

catch (EvalError e) { /* withCause IOException: fix permissions/regenerate expected file */ }

Prevention

When it happens

Trigger: Reading `X-expected.pcf` or `X-actual.pcf` fails: file deleted between the existence check and the read, unreadable permissions, locked file, or charset/IO error.

Common situations: Parallel processes cleaning generated files mid-test; expected-output file unreadable by the current user (wrong ownership after sudo-generated files); filesystem hiccup in containers or network mounts.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


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