netbirdio/netbird · error
login: %w
Error message
login: %w
What it means
Returned by Client.Start when authClient.Login fails while authenticating to the management server with the setup key or JWT token chosen in embed.New. The wrapped error is the management login error: rejected/expired/over-quota setup key, invalid JWT, unreachable or misbehaving management server, or TLS handshake failure.
Source
Thrown at client/embed/embed.go:265
ctx, cancel := context.WithCancel(internal.CtxInitState(context.Background()))
defer func() {
if c.connect == nil {
cancel()
}
}()
// nolint:staticcheck
ctx = context.WithValue(ctx, system.DeviceNameCtxKey, c.deviceName)
authClient, err := auth.NewAuth(ctx, c.config.PrivateKey, c.config.ManagementURL, c.config)
if err != nil {
return fmt.Errorf("create auth client: %w", err)
}
defer authClient.Close()
if err, _ := authClient.Login(ctx, c.setupKey, c.jwtToken); err != nil {
return fmt.Errorf("login: %w", err)
}
client := internal.NewConnectClient(ctx, c.config, c.recorder)
client.SetSyncResponsePersistence(true)
// either startup error (permanent backoff err) or nil err (successful engine up)
// TODO: make after-startup backoff err available
run := make(chan struct{})
clientErr := make(chan error, 1)
go func() {
if err := client.Run(run, ""); err != nil {
clientErr <- err
}
}()
select {
case <-startCtx.Done():
// ConnectClient.Stop now cancels its own run context and waits for the
// run loop to tear the engine down, so this cancel() is no longerView on GitHub (pinned to 93e97f4bf1)
Solutions
- Read the wrapped error to separate credential rejection (fix the credential) from connectivity (fix network/server).
- For setup keys: check status/expiry/usage limits in the management dashboard and generate a new key if needed.
- For JWT: fetch a fresh token immediately before Start instead of caching it long-term.
- For connectivity: verify the management URL responds (curl) and TLS trusts; retry Start with backoff if the server was temporarily down.
Example fix
// before
if err := client.Start(ctx); err != nil { log.Fatalf("start: %v", err) }
// after
if err := client.Start(ctx); err != nil {
if strings.Contains(err.Error(), "login:") {
// credential or management-side problem: refresh token/setup key, then retry
token, terr := fetchFreshJWT()
if terr == nil {
client, _ = embed.New(embed.Options{JWTToken: token, ...})
err = client.Start(ctx)
}
}
if err != nil { log.Fatalf("start: %v", err) }
} Defensive patterns
Strategy: retry
Try / catch
err := client.Start(ctx)
if err != nil && strings.Contains(err.Error(), "login:") {
// credential rejections are permanent: refresh token/setup key, recreate client, retry once
// connectivity errors: backoff and retry Start with the same client recreated Prevention
- Fetch JWTs immediately before Start; never cache long-lived tokens.
- Use setup keys with adequate expiry and usage headroom.
- Distinguish credential vs connectivity in the wrapped error before choosing retry vs re-register.
When it happens
Trigger: Client.Start after embed.New with a revoked or expired SetupKey, a JWTToken past expiry, a management URL pointing at a server that is down or returns an error, or clock skew breaking token validation.
Common situations: Setup keys rotated or deleted in the management dashboard while the embedding app kept the old one; JWTs fetched once and reused after expiry; management server behind a load balancer with a stale cert; running against a self-hosted management that is not yet up when the embedder starts.
Related errors
- create auth client: %w
- failed to check SSO support: %v
- failed to check login requirement: %v
- failed to create auth client: %v
- check login required: %v
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/251c04754b6635d8.
Report an issue: GitHub.