nats-io/nats-server · error

failed to be ready for connections after %s: startup did not

Error message

failed to be ready for connections after %s: startup did not complete

What it means

When running with `dont_listen` (in-process/server-embedded mode), there is no listener to probe, so ReadyForConnections polls s.startupComplete instead. If startup does not finish within the deadline, the caller gets this timeout error after %s (the waited duration).

Source

Thrown at server/server.go:4017

		chk["websocket"] = info{ok: (opts.Websocket.Port == 0 || s.websocket.listener != nil), err: s.websocket.listenerErr}
		chk["mqtt"] = info{ok: (opts.MQTT.Port == 0 || s.mqtt.listener != nil), err: s.mqtt.listenerErr}
		s.mu.RUnlock()

		var numOK int
		for _, inf := range chk {
			if inf.ok {
				numOK++
			}
		}
		if numOK == len(chk) {
			// In the case of DontListen option (no accept loop), we still want
			// to make sure that Start() has done all the work, so we wait on
			// that.
			if opts.DontListen {
				select {
				case <-s.startupComplete:
				case <-time.After(d):
					return fmt.Errorf("failed to be ready for connections after %s: startup did not complete", d)
				}
			}
			return nil
		}
		if d > 25*time.Millisecond {
			time.Sleep(25 * time.Millisecond)
		}
	}

	failed := make([]string, 0, len(chk))
	for name, inf := range chk {
		if inf.ok && inf.err != nil {
			failed = append(failed, fmt.Sprintf("%s(ok, but %s)", name, inf.err))
		}
		if !inf.ok && inf.err == nil {
			failed = append(failed, name)
		}
		if !inf.ok && inf.err != nil {

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Ensure Start() was actually invoked and completes: check logs for startup errors before waiting
  2. Increase the ReadyForConnections timeout or poll longer in embedded mode
  3. Fix whatever blocks startup (e.g. resolver URL unreachable) — inspect the wrapped startup logs
  4. Verify DontListen mode setup: with dont_listen the app must rely on in-process clients only

Example fix

// before
if !s.ReadyForConnections(500 * time.Millisecond) { ... }
// after
if !s.ReadyForConnections(10 * time.Second) {
    log.Fatalf("embedded server did not become ready")
}
Defensive patterns

Strategy: try-catch

Validate before calling

// In embedded (dont_listen) mode, confirm Start() was called and give a generous deadline
if !started.Flag("started") {
    return errors.New("call srv.Start() before ReadyForConnections")
}

Try / catch

if !srv.ReadyForConnections(30 * time.Second) {
    if opts.DontListen {
        log.Printf("startup did not complete within deadline; inspect server logs for blocking startup (resolver, options)")
        srv.Shutdown()
        return errors.New("embedded server startup timeout")
    }
    return errors.New("server not ready")
}

Prevention

When it happens

Trigger: Calling s.ReadyForConnections(d) (or NewServer+Start in embedded mode) with opts.DontListen true while Start() has not signaled startupComplete within the timeout — e.g. slow option processing, account resolver stalls, or a deadlock in startup.

Common situations: Embedded nats-server in tests/applications where Start() blocks on a slow resolver (URL fetch timeouts) or the caller never called Start(); too-short timeout (e.g. ReadyForConnections(time.Second)) under heavy init; startup goroutine panicking before signaling.

Related errors


AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02). Data as JSON: /api/errors/ee2ae3556309a4d5. Report an issue: GitHub.