apple/pkl · error · ProtocolException
unknownRequestId
unknownRequestId
Error message
unknownRequestId
What it means
When a Response message arrives, accept() looks up its requestId in the responseHandlers map and removes it. If no handler is registered for that requestId, a ProtocolException with code unknownRequestId is thrown, since the library cannot route the response to any pending request.
Solutions
- Check for duplicate or late responses from the peer and drop responses for unknown ids before calling accept()
- Verify the external reader generates unique, monotonic requestIds matching those sent
- Avoid cancelling/removing handlers while responses may still be in flight, or handle unknown ids gracefully
- Restart the whole transport session cleanly instead of reusing stale handler state
Example fix
// before
transport.accept(response); // throws unknownRequestId
// after
if (transport.hasPendingRequest(response.requestId())) {
transport.accept(response);
} else {
LOG.warn("Ignoring stale response for request " + response.requestId());
} Defensive patterns
Strategy: try-catch
Validate before calling
if (msg instanceof Message.Response r && !pendingRequests.containsKey(r.requestId())) { LOG.warn("dropping stale response " + r.requestId()); return; } Type guard
boolean isKnownResponse(Message m, Map<Long, ResponseHandler> pending) { return m instanceof Message.Response r && pending.containsKey(r.requestId()); } Try / catch
try { transport.accept(msg); } catch (ProtocolException e) { if ("unknownRequestId".equals(((ProtocolException) e).getErrorCode())) { LOG.warn("stale/duplicate response ignored"); } else { throw e; } } Prevention
- Track pending request ids and validate responses before accept()
- Remove handlers only after giving late responses a grace period
- Ensure peers generate unique request ids
- Restart transports fully rather than reusing handler maps
When it happens
Trigger: A Response arrives whose requestId was never registered, was already completed/removed (duplicate response), or whose handler map was cleared (e.g. after transport close/restart).
Common situations: Duplicate responses from a misbehaving external reader; requests cancelled/timed out locally and removed from the map just before the response arrives; requestId collisions after reusing or restarting a transport; version-mismatched peers generating wrong ids.
Related errors
- Cannot receive request messages before transport start.
- Cannot receive one-way messages before transport start.
- Unexpected incoming one-way message: $message
- Unexpected incoming request message: $message
- Cannot convert Pkl duration `this` to `java.time.Duration`.
AI-assisted analysis of apple/pkl@f3efcbfc9b (2026-09-08).
Data as JSON: /api/errors/beea45f7aa94f43c.
Report an issue: GitHub.
Appendix: source
Thrown at pkl-core/src/main/java/org/pkl/core/messaging/MessageTransports.java:163
logger.log(formatter.format(args));
}
protected abstract void doStart() throws ProtocolException, IOException;
protected abstract void doClose();
protected abstract void doSend(Message message) throws ProtocolException, IOException;
protected void accept(Message message) throws ProtocolException {
log("Received message: {0}", message);
if (message instanceof Message.OneWay msg) {
oneWayHandler.handleOneWay(msg);
} else if (message instanceof Message.Request msg) {
requestHandler.handleRequest(msg);
} else if (message instanceof Message.Response msg) {
var handler = responseHandlers.remove(msg.requestId());
if (handler == null) {
throw new ProtocolException(
ErrorMessages.create(
"unknownRequestId", message.getClass().getSimpleName(), msg.requestId()));
}
handler.handleResponse(msg);
}
}
@Override
public final void start(OneWayHandler oneWayHandler, RequestHandler requestHandler)
throws ProtocolException, IOException {
log("Starting transport: {0}", this);
this.oneWayHandler = oneWayHandler;
this.requestHandler = requestHandler;
doStart();
}
@Override
public final void close() {View on GitHub (pinned to f3efcbfc9b)