{"record":{"id":"24ce91eb5936f651","repo":"apple/pkl","slug":"cannot-receive-request-messages-before-transport-s","errorCode":null,"errorMessage":"Cannot receive request messages before transport start.","messagePattern":"Cannot receive request messages before transport start\\.","errorType":"exception","errorClass":"ProtocolException","httpStatus":null,"severity":"error","filePath":"pkl-core/src/main/java/org/pkl/core/messaging/MessageTransports.java","lineNumber":135,"sourceCode":"      assert other != null;\n      other.accept(message);\n    }\n\n    public void setOther(DirectMessageTransport other) {\n      this.other = other;\n    }\n  }\n\n  public abstract static class AbstractMessageTransport implements MessageTransport {\n\n    private final Logger logger;\n    private MessageTransport.OneWayHandler oneWayHandler =\n        (msg) -> {\n          throw new ProtocolException(\"Cannot receive one-way messages before transport start.\");\n        };\n    private MessageTransport.RequestHandler requestHandler =\n        (msg) -> {\n          throw new ProtocolException(\"Cannot receive request messages before transport start.\");\n        };\n    private final Map<Long, ResponseHandler> responseHandlers = new ConcurrentHashMap<>();\n\n    protected AbstractMessageTransport(Logger logger) {\n      this.logger = logger;\n    }\n\n    protected void log(String message, Object... args) {\n      var formatter = new MessageFormat(message);\n      logger.log(formatter.format(args));\n    }\n\n    protected abstract void doStart() throws ProtocolException, IOException;\n\n    protected abstract void doClose();\n\n    protected abstract void doSend(Message message) throws ProtocolException, IOException;\n","sourceCodeStart":117,"sourceCodeEnd":153,"githubUrl":"https://github.com/apple/pkl/blob/f3efcbfc9b60d30053b0536d664948d7aa1b8673/pkl-core/src/main/java/org/pkl/core/messaging/MessageTransports.java#L117-L153","documentation":"AbstractMessageTransport's default RequestHandler throws this ProtocolException when a request message arrives before the transport is started. Like the one-way variant, it indicates start() was never called, so no request handler was registered to serve incoming requests.","triggerScenarios":"A Message.Request arrives and is dispatched via accept() before start() replaced the default throwing requestHandler.","commonSituations":"Client connects and immediately sends a request while the server-side transport has not called start(); async initialization ordering bugs; tests constructing a transport and pumping messages without starting it.","solutions":["Call transport.start() before any message can be accepted on the connection","Delay accepting bytes/messages from the wire until handlers are registered","Add an integration test that connects and sends a request immediately to catch the race","In server code, only register the connection with the message loop after start() succeeds"],"exampleFix":"// before\ntransport.onConnection(conn -> conn.accept(msg)); // default handler throws\n// after\ntransport.start();\ntransport.onConnection(conn -> conn.accept(msg));","handlingStrategy":"validation","validationCode":"assert transportReady.get() : \"transport must be started before requests arrive\";","typeGuard":"null","tryCatchPattern":"try { transport.accept(request); } catch (ProtocolException e) { LOG.error(\"request before start()\"); transport.start(); /* then fail the request cleanly */ }","preventionTips":["Start the transport before accepting the network connection","Register request handlers in start() overrides","Gate the peer on a ready signal/handshake before sending requests","Test cold-start races (client sends immediately on connect)"],"tags":["java","messaging","lifecycle","protocol"],"backgroundTag":"invalid-state-transition","analyzedSha":"f3efcbfc9b60d30053b0536d664948d7aa1b8673","analyzedAt":"2026-09-08T13:10:45.570Z","contentChangedAt":"2026-09-08T13:10:45.570Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}