{"record":{"id":"7ec575437cbf0579","repo":"apple/pkl","slug":"unexpected-end-of-input-0-message-bytes","errorCode":null,"errorMessage":"Unexpected end of input; 0 message bytes","messagePattern":"Unexpected end of input; 0 message bytes","errorType":"exception","errorClass":"ProtocolException","httpStatus":null,"severity":"error","filePath":"libpkl/src/main/java/org/pkl/libpkl/NativeTransport.java","lineNumber":64,"sourceCode":"  protected void doSend(Message message) throws ProtocolException {\n    try (var os = new ByteArrayOutputStream();\n        var packer = MessagePack.newDefaultPacker(os)) {\n      var encoder = new ServerMessagePackEncoder(packer);\n      encoder.encode(message);\n      sendMessageToNative.accept(os.toByteArray());\n    } catch (IOException e) {\n      // impossible; no IO happens during packing\n      throw PklBugException.unreachableCode();\n    } catch (ProtocolException e) {\n      // should never happen; messages coming from Pkl should always be well-formed.\n      log(\"Unexpected protocol exception: \" + e);\n      throw e;\n    }\n  }\n\n  public void sendMessage(int length, CCharPointer ptr) throws ProtocolException {\n    if (length == 0) {\n      throw new ProtocolException(\"Unexpected end of input; 0 message bytes\");\n    }\n    try (var is = new NativeInputStream(length, ptr);\n        var unpacker = MessagePack.newDefaultUnpacker(is)) {\n      var message = new ServerMessagePackDecoder(unpacker).decode();\n      // guaranteed by `length == 0` check above\n      assert message != null;\n      accept(message);\n    } catch (IOException e) {\n      // impossible; no IO happens during unpacking\n      throw PklBugException.unreachableCode();\n    }\n  }\n}\n","sourceCodeStart":46,"sourceCodeEnd":78,"githubUrl":"https://github.com/apple/pkl/blob/f3efcbfc9b60d30053b0536d664948d7aa1b8673/libpkl/src/main/java/org/pkl/libpkl/NativeTransport.java#L46-L78","documentation":"NativeTransport.sendMessage decodes a message received from the native Pkl evaluator over the MessagePack IPC transport. The library throws this ProtocolException when the native side hands over a message whose declared length is 0, meaning there are no message bytes to decode at all. This signals a broken or prematurely closed protocol exchange between the JVM and the native evaluator rather than a decodable (even malformed) message.","triggerScenarios":"Calling sendMessage (or the underlying evaluator request path it serves) when the native peer passes length==0 into the callback — i.e. the native evaluator produced an empty frame, was shut down mid-conversation, or the transport was misinitialized.","commonSituations":"The Pkl native binary crashed or exited while the Java evaluator was still exchanging messages; a version mismatch between libpkl native library and the Java bindings; evaluator close/cancel racing an in-flight request.","solutions":["Check that the native Pkl evaluator process is still alive and inspect its stderr/stdout logs for a crash just before this error.","Verify the libpkl native library version matches the org.pkl.libpkl Java bindings version.","Ensure no code closes or cancels the evaluator concurrently with pending requests; serialize close after all messages are sent.","Enable verbose transport/protocol logging to capture the last successful frame before the empty one.","Retry the evaluation with a fresh Evaluator instance if the native side died."],"exampleFix":"// before: sending a message with a null/empty payload buffer\ntransport.sendMessage(0, null);\n// after: guard and only send when a real payload exists\nif (payloadBytes == 0 || ptr == null) {\n  throw new IllegalStateException(\"no encoded message to send\");\n}\ntransport.sendMessage(payloadBytes, ptr);","handlingStrategy":"try-catch","validationCode":"// before consuming the transport, verify the evaluator is alive\nif (!transport.isOpen() || evaluatorHandle == null) {\n  throw new IllegalStateException(\"native evaluator not started\");\n}\nif (length <= 0) {\n  log.error(\"native peer sent empty frame; evaluator likely crashed\");\n}","typeGuard":"static boolean hasPayload(int length, CCharPointer ptr) {\n  return length > 0 && ptr != null;\n}","tryCatchPattern":"try {\n  transport.sendMessage(length, ptr);\n} catch (ProtocolException e) {\n  log.error(\"broken native transport: {}\", e.getMessage(), e);\n  evaluator.close(); // native side is unusable; rebuild the evaluator\n  throw new EvaluationUnavailableException(e);\n}","preventionTips":["Pin the libpkl native library version to match the Java bindings","Monitor the native evaluator process and its stderr for early crashes","Never close/cancel the evaluator while requests are in flight","Log every protocol frame during development to spot empty frames early"],"tags":["native","ipc","protocol","messagepack","evaluator"],"backgroundTag":"unexpected-api-response-shape","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"}