apple/pkl · error · IOException
external read failure:
Error message
external read failure:
What it means
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.
Source
Thrown at pkl-core/src/main/java/org/pkl/core/messaging/MessageTransports.java:60
}
/** Creates "client" and "server" transports that are directly connected to each other. */
public static Pair<MessageTransport, MessageTransport> direct(Logger logger) {
var transport1 = new DirectMessageTransport(logger);
var transport2 = new DirectMessageTransport(logger);
transport1.setOther(transport2);
transport2.setOther(transport1);
return Pair.of(transport1, transport2);
}
public static <T extends @Nullable Object> T resolveFuture(Future<T> future) throws IOException {
try {
return future.get();
} catch (ExecutionException | InterruptedException e) {
if (e.getCause() instanceof IOException ioExc) {
throw ioExc;
} else {
throw new IOException("external read failure: " + e.getMessage(), e.getCause());
}
}
}
protected static class EncodingMessageTransport extends AbstractMessageTransport {
private final MessageDecoder decoder;
private final MessageEncoder encoder;
private volatile boolean isClosed = false;
protected EncodingMessageTransport(
MessageDecoder decoder, MessageEncoder encoder, Logger logger) {
super(logger);
this.decoder = decoder;
this.encoder = encoder;
}
@OverrideView on GitHub (pinned to f3efcbfc9b)
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
Example fix
// before
var result = transport.resolveFuture(future); // throws IOException("external read failure: ...")
// after
try {
var result = transport.resolveFuture(future);
} catch (IOException e) {
LOG.error("external reader failed", e.getCause());
throw new UncheckedIOException("External reader failed: " + e.getCause().getMessage(), e.getCause());
} Defensive patterns
Strategy: try-catch
Validate before calling
if (future.isCompletedExceptionally()) { future.exceptionally(ex -> { LOG.error("external transport will fail: ", ex); return null; }).join(); } Type guard
boolean isIoFailure(Throwable t) { Throwable c = t instanceof ExecutionException e ? e.getCause() : t; return c instanceof IOException; } Try / catch
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(); } Prevention
- 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
When it happens
Trigger: 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().
Common situations: 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.
Related errors
- Error converting property `%s` in Pkl object of type `%s` to
- externalReaderDoesNotSupportScheme
- e.getMessage()
- I/O error generating documentation: $e
- I/O error reading `${path.toUri()}`.
AI-assisted analysis of apple/pkl@f3efcbfc9b (2026-09-08).
Data as JSON: /api/errors/2aebf21085f5e1c8.
Report an issue: GitHub.