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?
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
Can I use this reproduction ?
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
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:
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)
ThreadStaticRepro.wasm
Using jco 1.16.1
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
WASI SDK 29
let me start with something basic, how do I install the latest jco, should I build from source?
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
and npm run build doesn't seem to find npx
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)
Solved one part the building of jco, as npx in windows is actually npx.cmd, the xshell.Shell needs npx.cmd for windows.
It seems to build successfully, but I can't see a jco executable.
Hey super late but you actually want @bytecodealliance/jco
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 ....
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)
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?
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;
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.
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.
https://github.com/bytecodealliance/jco/pull/1335
OK PR is looking good -- I'll get a new patch release out of Jco once it's merged
OK, please give this release a try:
https://www.npmjs.com/package/@bytecodealliance/jco/v/1.17.5
(works on my machine :)
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.
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
The good news is that with your latest code, it also works here
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:
initializecallsgetEnvironmentbecause 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 isgetEnvironment
Ah thank you! TIL. This makes a lot of sense.
Last updated: Apr 13 2026 at 00:25 UTC