netbirdio/netbird · error

engine is not running

Error message

engine is not running

What it means

extendAuthSession starts from an auth snapshot of the running client; when authSnapshot returns nil config or nil connect-client, the engine was never started or has been stopped (for example after logout), so there is no live session to extend. This is a precondition failure about client lifecycle, not a network or IdP problem.

Source

Thrown at client/android/session.go:283

	}
	ctx, cancel := context.WithCancel(context.Background())
	c.extendCancel = cancel
	return ctx, nil
}

func (c *Client) endExtend() {
	c.extendMu.Lock()
	defer c.extendMu.Unlock()
	if c.extendCancel != nil {
		c.extendCancel()
		c.extendCancel = nil
	}
}

func (c *Client) extendAuthSession(ctx context.Context, urlOpener URLOpener, isAndroidTV bool) error {
	cfg, cfgPath, cc := c.authSnapshot()
	if cfg == nil || cc == nil {
		return fmt.Errorf("engine is not running")
	}
	engine := cc.Engine()
	if engine == nil {
		return fmt.Errorf("engine is not initialized")
	}

	authClient, err := auth.NewAuth(ctx, cfg.PrivateKey, cfg.ManagementURL, cfg)
	if err != nil {
		return fmt.Errorf("failed to create auth client: %v", err)
	}
	defer authClient.Close()

	// Passing the config path makes the flow pick up the login_hint: an extend
	// renews the session of the account already signed in, so it must not stop to
	// offer a choice.
	a := NewAuthWithConfig(ctx, cfg, cfgPath)
	tokenInfo, err := a.foregroundGetTokenInfo(authClient, urlOpener, isAndroidTV)
	if err != nil {

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Only enable session renewal when the client is running and connected — Status() is neither Idle nor NeedsLogin
  2. After a logout or stop, require a full interactive login instead of extend
  3. If it races startup, wait for the connected state before invoking

Example fix

// before
client.ExtendAuthSession(opener, false, listener) // called regardless of client state

// after: gate on the run-loop status
if s := client.Status(); s != "Idle" && s != "NeedsLogin" {
	client.ExtendAuthSession(opener, false, listener)
}
Defensive patterns

Strategy: validation

Validate before calling

// Gate the renew action on the run-loop status before calling
String s = client.Status();
if (!"Idle".equals(s) && !"NeedsLogin".equals(s)) {
	client.ExtendAuthSession(opener, isTV, listener);
}

Type guard

fun canExtendSession(status: String): Boolean = status != "Idle" && status != "NeedsLogin"

Try / catch

// In the ErrListener of ExtendAuthSession
func (l *extendListener) OnError(err error) {
	if strings.Contains(err.Error(), "engine is not running") {
		// precondition failure: start/connect the client, or offer a full login instead
		return
	}
	l.show(err)
}

Prevention

When it happens

Trigger: ExtendAuthSession called before the client has started and connected (Status is Idle), after the engine was stopped, or after logout cleared the session — the snapshot has no cfg/cc to renew even though the session deadline may still be displayed.

Common situations: UI offering session renewal from a cached deadline after the tunnel went down, racing app startup before the connect loop is up, renew dialog still visible after the user logged out.

Related errors


AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16). Data as JSON: /api/errors/df4bbea6b2909ad1. Report an issue: GitHub.