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

  1. Guard every JS-to-Go callback with a check: if (go.exited) return; before invoking the wrapped function.
  2. Tear down JS-side event listeners, timers, and open requests in the .then/.catch of go.run().
  3. 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

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


AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12). Data as JSON: /api/errors/3ae447e0dd584187. Report an issue: GitHub.