golang/go · error · Error
Go program has already exited
Error message
Go program has already exited
What it means
Thrown by Go._resume() in wasm_exec.js when the Go program has already set its exited flag and a resume is attempted. The Go wasm runtime uses cooperative scheduling via exported resume(); once the program has called runtime.exit, further resumes would dereference freed state, so they are blocked. It indicates the host kept dispatching callback events after the guest finished.
Source
Thrown at lib/wasm/wasm_exec.js:557
});
// The linker guarantees global data starts from at least wasmMinDataAddr.
// Keep in sync with cmd/link/internal/ld/data.go:wasmMinDataAddr.
const wasmMinDataAddr = 4096 + 8192;
if (offset >= wasmMinDataAddr) {
throw new Error("total length of command line and environment variables exceeds limit");
}
this._inst.exports.run(argc, argv);
if (this.exited) {
this._resolveExitPromise();
}
await this._exitPromise;
}
_resume() {
if (this.exited) {
throw new Error("Go program has already exited");
}
this._inst.exports.resume();
if (this.exited) {
this._resolveExitPromise();
}
}
_makeFuncWrapper(id) {
const go = this;
return function () {
const event = { id: id, this: this, args: arguments };
go._pendingEvent = event;
go._resume();
return event.result;
};
}
}
})();View on GitHub (pinned to b6b368adc5)
Solutions
- Guard every JS-to-Go callback with a check: if (go.exited) return; before invoking the wrapped function.
- Tear down JS-side event listeners, timers, and open requests in the .then/.catch of go.run().
- If you must keep the runtime alive, ensure the Go program does not call os.Exit until all work is done.
Example fix
// before
const cb = go._makeFuncWrapper(id);
button.addEventListener('click', cb); // fires after Go exits -> throws
// after
const cb = go._makeFuncWrapper(id);
button.addEventListener('click', (...args) => {
if (go.exited) return;
return cb(...args);
}); Defensive patterns
Strategy: type-guard
Validate before calling
function safeCall(go, fn, ...args) {
if (go.exited) return undefined;
return fn(...args);
} Type guard
const isAlive = (go) => go.exited === false;
Prevention
- Tear down JS-side listeners, timers, and open requests in the .then/.finally of go.run().
- Guard every JS-to-Go callback entry point with a check on go.exited.
- Do not call go._resume() manually; let the runtime schedule resumes.
When it happens
Trigger: An async Go callback (wrapped via _makeFuncWrapper) is invoked after the Go program has exited; the host code calls go._resume() manually; an event loop fires a queued Go callback after go.run()'s promise resolved with an exit code.
Common situations: Long-lived JS callbacks (DOM events, setInterval, server requests) that route into Go-wasm functions after the program has shut down; test harnesses that resume a Go instance after it returned non-zero; forgetting to tear down JS-side listeners when the Go program exits.
Related errors
- globalThis.crypto is not available, polyfill required (crypt
- globalThis.performance is not available, polyfill required (
- globalThis.TextEncoder is not available, polyfill required
- globalThis.TextDecoder is not available, polyfill required
- Go.run: WebAssembly.Instance expected
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/3ae447e0dd584187.
Report an issue: GitHub.