Stream: jco

Topic: _getGlobalCurrentTaskMeta compatiblity?


view this post on Zulip Scott Waye (Mar 23 2026 at 13:35):

I'm trying to repro https://github.com/dotnet/runtimelab/issues/3247 with the latest version of wasi/wit-bindgen/nativeaot-llvm etc, but I'm hitting this error at runtime

C:\github\dotnet-3247-repro>node --stack-size=8192 test-host.mjs
file:///C:/github/dotnet-3247-repro/dist/ThreadStaticRepro.js:2081
  const { taskID } = _getGlobalCurrentTaskMeta(componentIdx);
          ^

TypeError: Cannot destructure property 'taskID' of '_getGlobalCurrentTaskMeta(...)' as it is undefined.
    at _lowerImportBackwardsCompat (file:///C:/github/dotnet-3247-repro/dist/ThreadStaticRepro.js:2081:11)
    at wasm://wasm/81ba884a:wasm-function[9]:0x112
    at ThreadStaticRepro.wasm.environment_get_environment (wasm://wasm/ThreadStaticRepro.wasm-01be54de:wasm-function[7685]:0x32f681)
    at ThreadStaticRepro.wasm.__wasilibc_initialize_environ (wasm://wasm/ThreadStaticRepro.wasm-01be54de:wasm-function[7664]:0x32f0c5)
    at ThreadStaticRepro.wasm.__wasilibc_initialize_environ_eagerly (wasm://wasm/ThreadStaticRepro.wasm-01be54de:wasm-function[7665]:0x32f220)
    at ThreadStaticRepro.wasm.__wasm_call_ctors (wasm://wasm/ThreadStaticRepro.wasm-01be54de:wasm-function[32]:0x363a)
    at ThreadStaticRepro.wasm._initialize (wasm://wasm/ThreadStaticRepro.wasm-01be54de:wasm-function[33]:0x3680)
    at _initGenerator (file:///C:/github/dotnet-3247-repro/dist/ThreadStaticRepro.js:7752:36)
    at _initGenerator.next (<anonymous>)
    at runNext (file:///C:/github/dotnet-3247-repro/dist/ThreadStaticRepro.js:7808:32)

Node.js v24.14.0

Is this an incompatibility between versions of jco and something else, wasi libc?

view this post on Zulip Victor Adossi (Mar 23 2026 at 15:51):

Hey Scott I'm not sure, but if I could get the component I could take a look and try to track it down. What version of Jco are you using?

To me it just looks like backwards compat that that broke/isn't working properly... But I'm not sure why it would be happening -- in general it seems like somehow an export was entered without a current task, but I can't say much more other than that

view this post on Zulip Victor Adossi (Mar 23 2026 at 15:52):

Can I use this reproduction ?

view this post on Zulip Victor Adossi (Mar 23 2026 at 16:11):

When I try to run through the reproduction in that repo above, I get a similar error as the user in that thread, but going all the way back to Jco 1.14.0, believe it or not:

Calling operation-a...
  CRASH on operation-a: null function or function signature mismatch
  Stack: RuntimeError: null function or function signature mismatch
    at .tmpwfZReW.GcAllocInternal(MethodTable*, unsigned int, unsigned long, Thread*) (wasm://wasm/.tmpwfZReW-009c7d96:wasm-function[110]:0x33d6)
    at .tmpwfZReW.RhpGcAlloc (wasm://wasm/.tmpwfZReW-009c7d96:wasm-function[109]:0x32f9)
    at .tmpwfZReW.AllocateObject(void*, MethodTable*, unsigned int, unsigned long) (wasm://wasm/.tmpwfZReW-009c7d96:wasm-function[923]:0x39139)
    at .tmpwfZReW.RhpNewArray (wasm://wasm/.tmpwfZReW-009c7d96:wasm-function[925]:0x3929a)
    at .tmpwfZReW.S_P_CoreLib_Internal_Runtime_ThreadStatics__GetUninlinedThreadStaticBaseForTypeSlow (wasm://wasm/.tmpwfZReW-009c7d96:wasm-function[1825]:0x7e2e0)
    at .tmpwfZReW.S_P_CoreLib_Internal_Runtime_ThreadStatics__GetUninlinedThreadStaticBaseForType (wasm://wasm/.tmpwfZReW-009c7d96:wasm-function[1378]:0x5d47c)
    at .tmpwfZReW.S_P_CoreLib_Internal_Runtime_ThreadStatics__GetThreadStaticBaseForType (wasm://wasm/.tmpwfZReW-009c7d96:wasm-function[1060]:0x44f9c)
    at .tmpwfZReW.__GetThreadStaticBase_S_P_CoreLib_System_Threading_ManagedThreadId (wasm://wasm/.tmpwfZReW-009c7d96:wasm-function[861]:0x38aef)
    at .tmpwfZReW.S_P_CoreLib_System_Threading_Lock__EnterAndGetCurrentThreadId (wasm://wasm/.tmpwfZReW-009c7d96:wasm-function[1388]:0x5e28c)
    at .tmpwfZReW.S_P_CoreLib_System_Runtime_CompilerServices_ClassConstructorRunner_Cctor__GetCctor (wasm://wasm/.tmpwfZReW-009c7d96:wasm-function[1826]:0x7e514)

Fatal error. Invalid Program: attempted to call a UnmanagedCallersOnly method from managed code.
  Follow-up call also failed: unreachable

Is there something you did to get the error you're looking at above? Is there something about my environment that doesn't match what you're doing?

I'm assuming that this issue does actually exist so I updated to the latest version of Jco 1.17.4 (just released) and I don't even see that line (_getGlobalCurrentTaskMeta(...) in the output at all

view this post on Zulip Victor Adossi (Mar 23 2026 at 16:28):

So we do use that intrinsic (that's used for async tasks) but maybe I'm building the example repro incorrectly because I don't get that intrinsic generated when I run the component. If you could give me the component directly that would save some time, but AFAICT I followed the build instructions correctly and ran dotnet correctly.

It seems like when your export is attempting to call an import it's failing around here:

https://github.com/bytecodealliance/jco/blob/49d5decb480954b9e0888b8722ec5738b45fd475/crates/js-component-bindgen/src/intrinsics/p3/async_task.rs#L2214

But upon entering the export there should have been a task already set...

That particular intrinsic is used by EnterSymmetricSyncGuestCall (upstream this is called Trampoline::EnterSyncCall) which is relatively new (compared to the rest of the machinery) -- is that implemented on your side? That should get called beforehand, and it's the intrinsic (on the Jco side) that sets that global current task meta.

That said, this feels like it might likely be a regression in that I need to make sure that Jco keeps working whether that trampoline is actually there or not (like it has in the past, I think if you can get the component to run properly on a previous version of Jco -- I could not)

view this post on Zulip Scott Waye (Mar 23 2026 at 17:16):

ThreadStaticRepro.wasm
Using jco 1.16.1

view this post on Zulip Scott Waye (Mar 23 2026 at 17:17):

I will upgrade jco to the latest and see what happens. I have started with that repo, but using latest wit-bindgen and nativeaot-llvm compiler as componentize-dotnet is a little out of date

view this post on Zulip Scott Waye (Mar 23 2026 at 17:18):

WASI SDK 29

view this post on Zulip Scott Waye (Mar 23 2026 at 18:08):

let me start with something basic, how do I install the latest jco, should I build from source?

view this post on Zulip Scott Waye (Mar 23 2026 at 18:09):

cargo install jco doesn't seem to be what I want
C:\github\jco>npx

Entering npm script environment at location:
C:\github\jco
Type 'exit' or ^D when finished

Microsoft Windows [Version 10.0.26200.7840]
(c) Microsoft Corporation. All rights reserved.

C:\github\jco>^D

view this post on Zulip Scott Waye (Mar 23 2026 at 18:10):

and npm run build doesn't seem to find npx


view this post on Zulip Scott Waye (Mar 23 2026 at 18:10):

C:\github\jco>npm run build

> build
> cargo xtask build debug

    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.12s
     Running `target\debug\xtask.exe build debug`
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.27s
Error: command not found: `npx`
error: process didn't exit successfully: `target\debug\xtask.exe build debug` (exit code: 1)

view this post on Zulip Scott Waye (Mar 23 2026 at 23:37):

Solved one part the building of jco, as npx in windows is actually npx.cmd, the xshell.Shell needs npx.cmd for windows.

view this post on Zulip Scott Waye (Mar 24 2026 at 00:22):

It seems to build successfully, but I can't see a jco executable.

view this post on Zulip Victor Adossi (Mar 24 2026 at 02:13):

Hey super late but you actually want @bytecodealliance/jco

view this post on Zulip Victor Adossi (Mar 24 2026 at 02:18):

So to install the latest Jco you should be able to:

npm install -D @bytecodealliance/jco

Or for a specific version:

npm install -D @bytecodealliance/jco@vX.X.X

What's a bit annoying is that npx will JIT download the package for the binary you're trying to run (in this case bare jco), but if you already have the binary installed then it will use the version in node_modules.

So the right command after it's installed is still npx jco ....

view this post on Zulip Victor Adossi (Mar 24 2026 at 02:19):

Also, it might be too much output, but you can turn set JCO_DEBUG=true to get lots of output from the transpiled component with regards to what it's doing (and crashes of logs should be in the right place roughly, order wise)

view this post on Zulip Victor Adossi (Mar 24 2026 at 09:18):

OK I think I get what's happening here -- the component that you provided here is calling the import during initialization -- has it/should it be wizer'd?

Simply importing the transpiled component causes it to attempt to perform a lowered import call (we never even get to attempt to do any operations that are included).

Is this intended? Normally a lowered import is called from inside a processed export (known at the CM level), _initialize is not a published export it's a core wasm export -- it's just being called during attempting to instantiate the "unbundled" module (specifically dist/ThreadStaticRepro.core.wasm, which gets named module3 in the generated code)

Note that the Wizer initialize entrypoint has also changed recently in Wizer versions -- from __wizer_initialize to wizer.initialize IIRC (and you can't call exports in the old non-component version of Wizer... You can in the newer versions though).

If I try and wasmtime wizer your component, I get the following output:

Error: failed to run main module `ThreadStaticRepro.wasm`

Caused by:
    0: Failed to parse invoke '_initialize()': See https://docs.wasmtime.dev/cli-options.html#run for syntax

Looking at the component it looks like initializing the runtime is what happens at the _initialize export, so it seems like you should be wizering.

Another question I'm wondering about is why your _initialize is attempting to call the getEnvironment (likely to do printing to stdout/stderr). Maybe something went wrong that isn't normally supposed to during runtime init?

view this post on Zulip Victor Adossi (Mar 24 2026 at 09:36):

To Sum up

Should your component be getting _initialize called & snapshotted via wizer? (this is one part of componentize-js, old and new)

I think my wizer command isn't working because by default wasmtime wizer isn't built with component model support, and you have a component with a core wasm export... which is essentially in between, if that makes sense. I'm not too sure about this so investigating now.

All this said, I can hack it such that bare wasm exports do work (though they're not supposed to, AFAIK, but maybe I'm wrong about this), by simply creating the task machinery it expects. The following code inside _lowerImportBackwardsCompat( will allow the component to run:

    let meta = _getGlobalCurrentTaskMeta(componentIdx);
    if (!meta) {
        const [newTask, ..._] = createNewCurrentTask({
           componentIdx,
          isAsync: false,
          entryFnName: 'custom',
          getCallbackFn: () => null,
          callbackFnName: 'null',
          errHandling: 'none',
          callingWasmExport: false,
        });

       _setGlobalCurrentTaskMeta({
         taskID: newTask.id(),
         componentIdx: newTask.componentIdx(),
       });

       meta = _getGlobalCurrentTaskMeta(componentIdx);
    }

  const { taskID } = meta;

view this post on Zulip Victor Adossi (Mar 24 2026 at 09:38):

I think the right solution here is wizering such that the runtime can init and get snapshotted into a new Wasm, then running that through.

view this post on Zulip Victor Adossi (Mar 24 2026 at 09:39):

Checking previous versions of Jco, this does indeed work (whether right or not!) so I'm going to make a change that enables this to work.

view this post on Zulip Victor Adossi (Mar 24 2026 at 09:53):

https://github.com/bytecodealliance/jco/pull/1335

view this post on Zulip Victor Adossi (Mar 24 2026 at 10:33):

OK PR is looking good -- I'll get a new patch release out of Jco once it's merged

view this post on Zulip Victor Adossi (Mar 24 2026 at 13:51):

OK, please give this release a try:

https://www.npmjs.com/package/@bytecodealliance/jco/v/1.17.5

(works on my machine :)

view this post on Zulip Scott Waye (Mar 24 2026 at 15:16):

Thanks a lot for the investigation Victor. I'm not familiar with wizer, we don't use it directly. Regards the initialization, I'll need to check. From memory the component will do this in the first entry. Problems here have happened in the past and I'll need to refresh my memory about where we are with those issues. In general it should be fine as it is in the wit-bindgen tests. Thanks for the updated version and the background on how it pulls in the version. I'll try to clean up my local modules, and see if I can get it to run the latest.

view this post on Zulip Scott Waye (Mar 25 2026 at 00:28):

initialize calls getEnvironment because this is a dotnet component, and there's a bunch of dotnet runtime stuff that needs to happen before we can call managed (dotnet) code even when that dotnet code is native aot'ed. One part of that stuff is getEnvironment

view this post on Zulip Scott Waye (Mar 25 2026 at 00:29):

The good news is that with your latest code, it also works here

view this post on Zulip Victor Adossi (Mar 25 2026 at 09:40):

Scott Waye said:

Thanks a lot for the investigation Victor. I'm not familiar with wizer, we don't use it directly. Regards the initialization, I'll need to check. From memory the component will do this in the first entry. Problems here have happened in the past and I'll need to refresh my memory about where we are with those issues. In general it should be fine as it is in the wit-bindgen tests. Thanks for the updated version and the background on how it pulls in the version. I'll try to clean up my local modules, and see if I can get it to run the latest.

Ah thanks for pointing this out -- I wasn't so familiar with the C# toolchain so was curious.

Probably more of a file-a-ticket-and-do-it-later thing, I think Wizer should offer an improvement in setup time (obviously) once it's implemented, assuming snapshotting goes fine. Definitely not a must have though, clearly recent changes to Jco broke what was working just fine before.

Scott Waye said:

initialize calls getEnvironment because this is a dotnet component, and there's a bunch of dotnet runtime stuff that needs to happen before we can call managed (dotnet) code even when that dotnet code is native aot'ed. One part of that stuff is getEnvironment

Ah thank you! TIL. This makes a lot of sense.


Last updated: Apr 13 2026 at 00:25 UTC