diff --git a/plugin/wasmguest/exports.go b/plugin/wasmguest/exports.go index df8618a..831b6a8 100644 --- a/plugin/wasmguest/exports.go +++ b/plugin/wasmguest/exports.go @@ -77,10 +77,19 @@ func bnInvoke(hookID uint32, ptr uint32, length uint32) uint64 { // bn_invoke returns). Release it whether or not the host calls bn_free. delete(pinned, ptr) - lastResponse = resp + // A logically-empty InvokeResponse (Error nil + empty payload — e.g. + // LoadResponse/UnloadResponse) proto-marshals to ZERO bytes. That is a + // valid success envelope, not the "couldn't produce an envelope" sentinel, + // so frame it as (ptr, 0) with a real pointer. Returning packed 0 here + // would make the host mistake every empty-response hook (notably a + // successful LOAD) for an INTERNAL failure and discard the instance. A + // 1-byte backing keeps the pointer valid while the reported length stays 0 + // (the host reads zero bytes → an empty InvokeResponse, which is correct). if len(resp) == 0 { - return 0 + lastResponse = []byte{0} + return uint64(bufPtr(lastResponse)) << 32 } + lastResponse = resp return uint64(bufPtr(resp))<<32 | uint64(uint32(len(resp))) }