Stream: general

Topic: [browser] poll_oneoff - Not a valid "Pollable"


view this post on Zulip Ed (Oct 30 2025 at 01:30):

TLDR: when my component calls WASI poll_oneoff it fails with "Not a valid 'Pollable' resource"

This is a real hack, but I hope I can be pointed in the right direction as I can't find any resources for more info on this. Background: this project is to perform patching on binary firmware files for industrial equipment. We have a working prototype via a container and need to get it out to the front line

We use Container2WASM (https://github.com/container2wasm/container2wasm) to build the container into a WASM module (which uses WASI Preview1), then call wasm-tools to make that into a component (using the wasm1 console adapter) and then call JCO to transpile that.

The core loads and starts, but once the VM starts polling, it fails witht he following exception:

[PatchFirmware] Error: TypeError: Resource error: Not a valid "Pollable" resource.

at trampoline6 (firmwarepatcher.js:862:11)

at poll_oneoff (firmwarepatcher.core2.wasm:0x54a6)

at abba7226:0x2b0

at __imported_wasi_snapshot_preview1_poll_oneoff (firmwarepatcher.core.wasm:0xae85)

at __wasi_poll_oneoff (firmwarepatcher.core.wasm:0x70787)

at pselect (firmwarepatcher.core.wasm:0x6be92)

at select (firmwarepatcher.core.wasm:0x6c045)

at virt_machine_run (firmwarepatcher.core.wasm:0x9693)

at main (firmwarepatcher.core.wasm:0xabc5)

at __main_void (firmwarepatcher.core.wasm:0x6c4dc)

Previously we were using browser_wasi_shim with C2W (https://github.com/bjorn3/browser_wasi_shim) which handled the polls correctly. We switched as that package lacks full WASI support, unlike JCO which we have successfully used for other (less janky) projects. But we haven't used it for command/CLI before, and wonder if we are missing a stdin/stdout which is causing the issue?

We are working on a proper solution... I promise! But for now, we need to get this working, so any advice would be great.

Here is the full build process:

docker build -t dk-patcher:latest --platform=linux/riscv64 .
c2w --target-arch=riscv64 -- dk-patcher:latest ./temptopatch.wasm
wasm-tools component new ./temptopatch.wasm --adapt wasi_snapshot_preview1=adapter.wasm -o temp.component.wasm
bunx @bytecodealliance/jco transpile ./temp.component.wasm -o ../webserver/src/lib/patcher --name 'dashkeepfirmwarepatcher' --no-namespaced-exports --async-mode jspi

Basic launch code:

import { expose } from "comlink";
import { instantiate } from "$lib/patcher/firmwarepatcher";
import {
  WASIShim,
  type WASIImportObject,
  type VersionedWASIImportObject,
} from "@bytecodealliance/preview2-shim/instantiation";

const wasmModules = import.meta.glob("$lib/patcher/*.wasm", {
  as: "url",
});

export type TPatchFirmware = typeof PatchFirmware;

async function PatchFirmware(
  firmwareBuff: ArrayBuffer,
  onStdout?: (text: string) => void,
) {
  try {
    console.log("[PatchFirmware] Starting firmware patching process");

    async function loader(path: string) {
      return await WebAssembly.compileStreaming(
        fetch(await wasmModules[`/src/lib/patcher/${path}`]()),
      );
    }

    const shimCore = new WASIShim();
    // for importing directly, i.e. wasi:cli/blah
    const shim = shimCore.getImportObject();
    // For importing when a version is defined, i.e. wasi:cli/blah@0.2.6
    const shimVersioned: VersionedWASIImportObject<"0.2.6"> =
      shimCore.getImportObject({ asVersion: "0.2.6" });
    shim satisfies WASIImportObject;
    shim satisfies VersionedWASIImportObject<"">;
    shimVersioned satisfies VersionedWASIImportObject<"0.2.6">;

    // @ts-ignore - We have provided it everything it needs (except for debug provisions)
    const instance = await instantiate(loader, {
      ...shim,
      ...shimVersioned,
    });

    const result = instance.run.run();

    console.log(result);

    return {
      success: true,
      message: "Firmware patched successfully",
      result,
    };
  } catch (error) {
    console.error("[PatchFirmware] Error:", error);
    throw error;
  }
}

expose(PatchFirmware);
Container to WASM converter. Contribute to container2wasm/container2wasm development by creating an account on GitHub.
A WASI shim for in the browser. Contribute to bjorn3/browser_wasi_shim development by creating an account on GitHub.

view this post on Zulip Alex Crichton (Nov 01 2025 at 23:19):

This could range anywhere from a bug in the original code to a bug in c2w to a bug in wasm-tools to a bug in jco to a bug in browsers. If the starting point is an entire container compiled to wasm it's going to be quite difficult to try to reduce that at all or narrow down what's going on. My only wild guess at this point would be that it's UB and some out-of-bounds write or something like that corrupting the state of the wasi adapter. I have no real reason to suspect that over anything else, though.

view this post on Zulip Ed (Nov 02 2025 at 00:35):

Thanks @Alex Crichton - I figured it was predominantly due to the browser implementation being essentially in a skeleton state. It worked on the node version fine. I did try to shim, which did work but added a bit too much complexity, so ended up taking a different approach. I think instead of JCO having separate node and browser code versions they should follow the same codebase with different compatible dependencies

view this post on Zulip Victor Adossi (Nov 05 2025 at 16:57):

Hey @Ed sorry a bit late here, but didn't realize this was about Jco -- yes the browser code is still very experimental (and is just missing some bits), so if things worked fine in the Node version, this is almost surely the browser shims not being up to snuff.

The community does use some other shims for the browser (so you could look at those), but also happy to take any PRs if you're motivated to improve the browser shims! :bow:


Last updated: Dec 06 2025 at 05:03 UTC