Detecting Traps
While some errors are represented at the WIT type level and expected, some internal errors
may cause a component instance to trap. Jco-generated bindings report the
component-model traps they detect with the built-in WebAssembly.RuntimeError class.
A trap that disables a component instance makes subsequent calls throw the original trap without re-entering the component. A trap in a background task can end only that task; it does not always disable the entire instance.
Catch traps from exported calls
To detect these traps, use the built-in WebAssembly.RuntimeError class. For bindings that
use ComponentError wrapping, handle declared WIT errors separately with isComponentError():
import { instantiate } from './dist/transpiled/component.js';
import { WASIShim } from '@bytecodealliance/preview2-shim/instantiation';
const shim = new WASIShim().getImportObject();
const instance = await instantiate(undefined, shim);
try {
await instance['ns:pkg/iface'].someFunction();
} catch (err) {
if (instance._util.isComponentError(err)) {
console.error('Declared WIT error:', err.payload);
} else if (err instanceof WebAssembly.RuntimeError) {
console.error(`TRAP: ${err}`);
// Release runtime-managed resources and end any remaining guest work.
instance._util.stop();
} else {
// Other exceptions are unexpected and should be handled separately.
throw err;
}
}
Traps are not ComponentError instances. Jco reports component-model traps as
WebAssembly.RuntimeError, a distinct error type. Declared WIT result::err values are part
of the component’s normal return contract; when wrapping is enabled, they are thrown as
ComponentError instances instead. _util.isComponentError(error) returns false for traps.
For direct ES module output, import _util from the generated module and call
_util.isComponentError(error). The predicate is only generated when the bindings use
error wrapping. See WIT result return values
for wrapping defaults and payload handling, and Generated utilities
for the _util overview.
Using await inside the try block catches both synchronous exceptions and rejected
asynchronous calls. It cannot catch a trap in guest work that continues after the call
has returned.
Receive background traps with onTrap
When generating bindings with --instantiation async or --instantiation sync, pass an
onTrap callback in the fourth argument to instantiate. The callback receives each trap
once, including traps in background tasks and deadlocks. It also receives traps that throw
or reject an exported call, so catching that call is still necessary.
For example, a Node.js host using asynchronous instantiation can log traps even after
someFunction() has returned:
import { readFile } from 'node:fs/promises';
import { instantiate } from './dist/transpiled/component.js';
import { WASIShim } from '@bytecodealliance/preview2-shim/instantiation';
const getCoreModule = async (path) =>
WebAssembly.compile(await readFile(new URL(`./dist/transpiled/${path}`, import.meta.url)));
const instance = await instantiate(
getCoreModule,
new WASIShim().getImportObject(),
undefined, // Use the default core Wasm instantiator.
{
onTrap(error) {
console.error('Component trapped:', error);
},
},
);
try {
await instance['ns:pkg/iface'].someFunction();
} catch (error) {
// End remaining guest work before propagating the failed call.
instance._util.stop();
throw error;
}
// Background guest work can still report traps through onTrap after this point.
The callback belongs to this instantiation, including its nested components. Other
instantiations have their own callbacks. Without onTrap, traps retain their existing
behavior, but a background trap may have no outstanding host call to reject.
onTrap runs synchronously while the runtime unwinds the trap. It may call
instance._util.stop() to end the remaining guest work, but it should not call other guest
exports. Stopping an instance does not notify onTrap, and no further notifications are
sent after it is stopped. See Stopping running instances
for cleanup and host-owned I/O responsibilities.
If onTrap throws, its exception is rethrown in a microtask as an uncaught exception so
the runtime can finish unwinding the original trap. Keep the callback from throwing when
using it for logging or cleanup. Host imports throwing an error other than
WebAssembly.RuntimeError do not trigger onTrap.