{"record":{"id":"2aebf21085f5e1c8","repo":"apple/pkl","slug":"external-read-failure","errorCode":null,"errorMessage":"external read failure: ","messagePattern":"external read failure: ","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"pkl-core/src/main/java/org/pkl/core/messaging/MessageTransports.java","lineNumber":60,"sourceCode":"  }\n\n  /** Creates \"client\" and \"server\" transports that are directly connected to each other. */\n  public static Pair<MessageTransport, MessageTransport> direct(Logger logger) {\n    var transport1 = new DirectMessageTransport(logger);\n    var transport2 = new DirectMessageTransport(logger);\n    transport1.setOther(transport2);\n    transport2.setOther(transport1);\n    return Pair.of(transport1, transport2);\n  }\n\n  public static <T extends @Nullable Object> T resolveFuture(Future<T> future) throws IOException {\n    try {\n      return future.get();\n    } catch (ExecutionException | InterruptedException e) {\n      if (e.getCause() instanceof IOException ioExc) {\n        throw ioExc;\n      } else {\n        throw new IOException(\"external read failure: \" + e.getMessage(), e.getCause());\n      }\n    }\n  }\n\n  protected static class EncodingMessageTransport extends AbstractMessageTransport {\n\n    private final MessageDecoder decoder;\n    private final MessageEncoder encoder;\n    private volatile boolean isClosed = false;\n\n    protected EncodingMessageTransport(\n        MessageDecoder decoder, MessageEncoder encoder, Logger logger) {\n      super(logger);\n      this.decoder = decoder;\n      this.encoder = encoder;\n    }\n\n    @Override","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/apple/pkl/blob/f3efcbfc9b60d30053b0536d664948d7aa1b8673/pkl-core/src/main/java/org/pkl/core/messaging/MessageTransports.java#L42-L78","documentation":"This IOException is thrown by MessageTransports.resolveFuture when a transport request future completes exceptionally. If the underlying cause is an IOException it is rethrown as-is; otherwise the original message is wrapped as \"external read failure: <message>\" with the original cause attached. It signals that the external process/transport backing an evaluation (e.g. an external reader) failed while reading a response.","triggerScenarios":"A MessageTransport request future (e.g. from an external reader process handling a module/resource read) completes with an ExecutionException whose cause is not an IOException, and the caller calls resolveFuture which blocks on future.get().","commonSituations":"External reader processes crashing or timing out mid-request; class-casting or protocol errors inside the transport handler; a dependency of the external reader throwing an unexpected exception type; killed child processes during evaluation.","solutions":["Inspect the wrapped cause (getCause()) to find the real failure in the external reader process","Verify the external reader process is alive, on the supported protocol version, and not crashing on the requested resource","Retry the evaluation; transient process/IPC failures often resolve on retry","Catch IOException at the evaluation call site and surface the cause chain to the user","Upgrade Pkl / the external reader to matching versions if a protocol mismatch is suspected"],"exampleFix":"// before\nvar result = transport.resolveFuture(future); // throws IOException(\"external read failure: ...\")\n// after\ntry {\n  var result = transport.resolveFuture(future);\n} catch (IOException e) {\n  LOG.error(\"external reader failed\", e.getCause());\n  throw new UncheckedIOException(\"External reader failed: \" + e.getCause().getMessage(), e.getCause());\n}","handlingStrategy":"try-catch","validationCode":"if (future.isCompletedExceptionally()) { future.exceptionally(ex -> { LOG.error(\"external transport will fail: \", ex); return null; }).join(); }","typeGuard":"boolean isIoFailure(Throwable t) { Throwable c = t instanceof ExecutionException e ? e.getCause() : t; return c instanceof IOException; }","tryCatchPattern":"try { var v = resolveFuture(future); } catch (IOException e) { Throwable cause = e.getCause(); LOG.error(\"external read failed\", cause); throw new UncheckedIOException(e); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new CancellationException(); }","preventionTips":["Always unwrap and log getCause() to see the real external failure","Health-check the external reader process before evaluations","Pin compatible versions of Pkl and external readers","Retry transient external reads with backoff"],"tags":["java","io","external-reader","wrapped-exception"],"backgroundTag":"network-request-failed","analyzedSha":"f3efcbfc9b60d30053b0536d664948d7aa1b8673","analyzedAt":"2026-09-08T13:10:45.570Z","contentChangedAt":"2026-09-08T13:10:45.570Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}