evanw/esbuild · error

The service was stopped

Error message

The service was stopped

What it means

Returned from an OnEnd plugin callback running inside the esbuild child-process service (cmd/esbuild run with --service, spawned by esbuild's JS API). It fires when service.sendRequest() fails its type assertion to map[string]interface{}, which happens when the in-memory request channel never receives a real response because the JS host process has closed the pipe / torn down the service. The callback can no longer relay build-end results back to JS, so it short-circuits with this sentinel error. It is esbuild's way of aborting plugin callbacks that are stranded after a context dispose or process exit.

Source

Thrown at cmd/esbuild/service.go:740

					//
					// This is especially important if "write" is false since otherwise
					// we'd unnecessarily send the entire contents of all output files!
					//
					//          "If a tree falls in a forest and no one is
					//           around to hear it, does it make a sound?"
					//
					activeBuild.mutex.Lock()
					isWithinRebuild := activeBuild.withinRebuildCount > 0
					activeBuild.mutex.Unlock()
					if !hasOnEndCallbacks && !isWithinRebuild && !writeToStdout {
						return api.OnEndResult{}, nil
					}
					request := resultToResponse(*result)
					request["command"] = "on-end"
					request["key"] = key
					response, ok := service.sendRequest(request).(map[string]interface{})
					if !ok {
						return api.OnEndResult{}, errors.New("The service was stopped")
					}
					var errors []api.Message
					var warnings []api.Message
					if value, ok := response["errors"].([]interface{}); ok {
						errors = decodeMessages(value)
					}
					if value, ok := response["warnings"].([]interface{}); ok {
						warnings = decodeMessages(value)
					}
					return api.OnEndResult{
						Errors:   errors,
						Warnings: warnings,
					}, nil
				})
			},
		})

		ctx, err := api.Context(options)

View on GitHub (pinned to 6ff1d8b0d8)

Solutions

  1. Ensure you do not call context.dispose() (or let the process exit) while a build is still running — await the rebuild() promise before disposing.
  2. If tearing down on purpose, treat 'The service was stopped' as expected and swallow it in your plugin/teardown path rather than surfacing it.
  3. Guard plugin onEnd logic so it degrades gracefully if the result indicates shutdown (the error is informational, not a build failure).
  4. If unexpected, check for an unhandled exception in your JS-side onEnd callback that is crashing the service.

Example fix

// before
const result = await ctx.rebuild();
ctx.dispose(); // may race with pending onEnd plugin relay

// after
const result = await ctx.rebuild();
// let onEnd callbacks finish before disposing
ctx.dispose();
Defensive patterns

Strategy: try-catch

Validate before calling

// Ensure no in-flight build before disposing
let pending = null;
async function safeDispose(ctx) {
  if (pending) await pending.catch(() => {});
  ctx.dispose();
}
pending = ctx.rebuild();
await safeDispose(ctx);

Type guard

function isServiceStoppedError(e) {
  return e && typeof e.message === 'string' && e.message === 'The service was stopped';
}

Try / catch

try { await ctx.rebuild(); } catch (e) { if (!isServiceStoppedError(e)) throw e; /* expected during teardown */ }

Prevention

When it happens

Trigger: A Go-shim OnEnd callback (registered via build.OnEnd in the service bridge) calls service.sendRequest({command:"on-end"...}) and the returned value is not a map — i.e. the JS side never replied. This occurs when ctx.Dispose() (or process kill / unhandled rejection) happens while a build with plugins is still finishing, or when the stdio connection to the JS parent is severed mid-build.

Common situations: Calling esbuild context .dispose() while a build is in flight and the build has an onEnd plugin; the JS host crashing or being SIGKILLed during a build; hot-reload tooling that disposes and recreates esbuild contexts rapidly; CI timeouts killing the esbuild process mid-build.

Related errors


AI-assisted analysis of evanw/esbuild@6ff1d8b0d8 (2026-08-03). Data as JSON: /data/errors/602737caabe28e69.json. Report an issue: GitHub.