apple/pkl · error · ProtocolException
Unexpected incoming request message: ${msg}
Error message
Unexpected incoming request message: ${msg} What it means
runTransport also registers a handler for incoming request messages from the reader; the host never expects to serve requests to the reader, so any incoming request is a protocol violation and throws ProtocolException wrapped in a RuntimeException in the rxThread. This protects the host from readers that misuse the request channel.
Source
Thrown at pkl-core/src/main/java/org/pkl/core/externalreader/ExternalReaderProcessImpl.java:149
rxThread.start();
return transport;
}
}
/**
* Runs the underlying message transport so it can receive responses from the child process.
*
* <p>Blocks until the underlying transport is closed.
*/
private void runTransport(MessageTransport transport) {
try {
transport.start(
(msg) -> {
throw new ProtocolException("Unexpected incoming one-way message: " + msg);
},
(msg) -> {
throw new ProtocolException("Unexpected incoming request message: " + msg);
});
} catch (ProtocolException | IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void close() {
synchronized (lock) {
if (closed) return;
closed = true;
try {
if (transport != null && process != null && process.isAlive()) {
transport.send(new CloseExternalProcess());
process.waitFor(CLOSE_TIMEOUT.toMillis(), TimeUnit.MILLISECONDS);
}
} catch (Exception ignored) {View on GitHub (pinned to f3efcbfc9b)
Solutions
- Align protocol versions between pkl-core and the external reader binary
- Fix the reader implementation to send only one-way messages/responses, never requests
- Log the offending message to identify which request type was sent
- Capture a transport trace to check for framing desynchronization
Defensive patterns
Strategy: try-catch
Try / catch
try {
reader.use { ... }
} catch (RuntimeException e) {
if (e.getCause() instanceof ProtocolException pe && pe.getMessage().startsWith("Unexpected incoming request message")) {
// reader sent a request; align protocol versions or fix the reader
}
} Prevention
- Use only spec-conformant readers: they must never send request messages to the host
- Version-check the reader binary at startup before evaluation
- Record a transport trace when developing a custom reader
- Pin reader versions in deployment to avoid silent protocol drift
When it happens
Trigger: The external reader process sends a request-type message to the host — e.g. a buggy or newer reader expecting bidirectional requests, or stream corruption making a reply parse as a request.
Common situations: Protocol version mismatch between pkl-core and the reader binary, custom reader implementations issuing requests, desynchronized framing after an error.
Related errors
- Unexpected incoming one-way message: ${msg}
- Unexpected end of input; 0 message bytes
- externalReaderAlreadyTerminated
- Node `%s` of type `%s` does not have a property named `%s`.
- JavaType token must be parameterized.
AI-assisted analysis of apple/pkl@f3efcbfc9b (2026-09-08).
Data as JSON: /api/errors/dcf8e4d5da340415.
Report an issue: GitHub.