netbirdio/netbird · warning

service is not up

Error message

service is not up

What it means

Exported sentinel ErrServiceNotUp (client/server/server.go:62), returned by Server.cleanupConnection when s.actCancel is nil, i.e. the daemon has no active connection context. It signals that a teardown-type operation (Down, Logout) was requested while the service was already down; call sites in server.go explicitly match it with errors.Is to treat this case as benign.

Source

Thrown at client/server/server.go:62

	retryInitialIntervalVar = "NB_CONN_RETRY_INTERVAL_TIME"
	maxRetryIntervalVar     = "NB_CONN_MAX_RETRY_INTERVAL_TIME"
	maxRetryTimeVar         = "NB_CONN_MAX_RETRY_TIME_TIME"
	retryMultiplierVar      = "NB_CONN_RETRY_MULTIPLIER"
	defaultInitialRetryTime = 30 * time.Minute
	defaultMaxRetryInterval = 60 * time.Minute
	defaultMaxRetryTime     = 14 * 24 * time.Hour
	defaultRetryMultiplier  = 1.7

	// JWT token cache TTL for the client daemon (disabled by default)
	defaultJWTCacheTTL = 0

	errRestoreResidualState   = "failed to restore residual state: %v"
	errProfilesDisabled       = "profiles are disabled, you cannot use this feature without profiles enabled"
	errUpdateSettingsDisabled = "update settings are disabled, you cannot use this feature without update settings enabled"
	errNetworksDisabled       = "network selection is disabled by the administrator"
)

var ErrServiceNotUp = errors.New("service is not up")

// Server for service control.
type Server struct {
	rootCtx   context.Context
	actCancel context.CancelFunc

	logFile string

	// uiLogPath is the desktop UI's absolute log path, reported via
	// RegisterUILog. Guarded by mutex. Consumed by DebugBundle so the bundle
	// can collect the GUI log even though the daemon runs as root and can't
	// resolve the user's config dir. Last-writer-wins (one UI per socket).
	// DebugBundle opens it on behalf of the bundle requester and refuses a file
	// that caller does not own, so a local user cannot read another user's log
	// or a root-only file through it.
	uiLogPath string

	oauthAuthFlow oauthAuthFlow

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Treat it as a no-op: the desired state (service down) already holds.
  2. Check the daemon status before issuing Down to avoid the call entirely.
  3. If you expected the service to be up, inspect logs for why the connection context was never created (failed login/up).

Example fix

// before: surfacing it as a hard failure
err := server.Down(ctx)
if err != nil {
    return err // "service is not up" breaks idempotent scripts
}

// after: idempotent down
if err := server.Down(ctx); err != nil && !errors.Is(err, server.ErrServiceNotUp) {
    return err
}
Defensive patterns

Strategy: validation

Validate before calling

// Only issue Down when there is something to bring down.
status, err := daemonClient.Status(ctx)
if err != nil {
    return err
}
if status.GetStatus() == daemonpb.StatusEnum_DOWN {
    return nil // desired state already reached
}

Type guard

func isServiceNotUp(err error) bool {
    return errors.Is(err, server.ErrServiceNotUp) // in-process Go callers
}

Try / catch

if err := server.Down(ctx); err != nil {
    if server.IsServiceNotUp(err) {
        return nil // already down: idempotent success
    }
    return err
}

Prevention

When it happens

Trigger: Invoking the Down or Logout RPC when the agent is not connected (never started, already stopped, or login never completed) makes cleanupConnection hit the nil actCancel guard and return this sentinel.

Common situations: Calling netbird down twice; UI and CLI both issuing Down; scripting that unconditionally runs down after a failed up; MDM policies issuing down on a freshly installed, never-connected agent.

Related errors


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