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.
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
- ErrWriteAfterClose
- globalThis.crypto is not available, polyfill required…
- globalThis.performance is not available, polyfill required…
- globalThis.TextDecoder is not available, polyfill required
- globalThis.TextEncoder is not available, polyfill required
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/3ae447e0dd584187.
Report an issue: GitHub.
Appendix: 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)