netbirdio/netbird · warning
client already started
Error message
client already started
What it means
First branch of forwardWithHeaderAuth's token handling (proxy/internal/auth/middleware.go:472). When a request presents a header scheme token and mw.validateSessionToken returns a non-nil error that is NOT errValidationUnavailable, the token cannot be processed locally (Ed25519 signature verify failure, malformed token) and the client gets 400 'invalid session token'.
Source
Thrown at client/embed/embed.go:32
"github.com/sirupsen/logrus"
wgdevice "golang.zx2c4.com/wireguard/device"
wgnetstack "golang.zx2c4.com/wireguard/tun/netstack"
"github.com/netbirdio/netbird/client/iface"
"github.com/netbirdio/netbird/client/iface/netstack"
"github.com/netbirdio/netbird/client/internal"
"github.com/netbirdio/netbird/client/internal/auth"
"github.com/netbirdio/netbird/client/internal/peer"
"github.com/netbirdio/netbird/client/internal/profilemanager"
sshcommon "github.com/netbirdio/netbird/client/ssh"
"github.com/netbirdio/netbird/client/system"
"github.com/netbirdio/netbird/shared/management/domain"
mgmProto "github.com/netbirdio/netbird/shared/management/proto"
"github.com/netbirdio/netbird/util/capture"
)
var (
ErrClientAlreadyStarted = errors.New("client already started")
ErrClientNotStarted = errors.New("client not started")
ErrEngineNotStarted = errors.New("engine not started")
ErrConfigNotInitialized = errors.New("config not initialized")
)
const (
// PeerStatusConnected indicates the peer is in connected state.
PeerStatusConnected = peer.StatusConnected
)
// PeerConnStatus is a peer's connection status.
type PeerConnStatus = peer.ConnStatus
// Client manages a netbird embedded client instance.
type Client struct {
deviceName string
config *profilemanager.Config
mu sync.MutexView on GitHub (pinned to 93e97f4bf1)
Solutions
- Obtain a fresh session token by completing the domain's auth flow again, and send it exactly as issued (no truncation, no extra whitespace).
- Confirm the token was issued for this domain: session tokens are validated against the per-domain SessionPublicKey registered via AddDomain.
- If the error started after a key change or management/proxy reconfiguration, re-issue tokens and verify the public key configured for the domain matches the management signing key.
- Check the proxy log for the underlying validateSessionToken error to distinguish 'malformed' from 'signature verification failed'.
Example fix
# before: stale/mangled token curl -H 'X-Api-Token: abc123…truncated' https://app.example.com/ # -> 400 invalid session token # after: freshly issued token, sent verbatim curl -H "X-Api-Token: $NB_SESSION_TOKEN" https://app.example.com/
Defensive patterns
Strategy: validation
Validate before calling
// Before sending: cheap sanity check that the token is the expected shape
// (opaque session token, no stray whitespace/newlines).
func looksLikeSessionToken(tok string) bool {
tok = strings.TrimSpace(tok)
return tok != "" && !strings.ContainsAny(tok, " \t\r\n")
} Try / catch
resp, err := client.Do(req)
if err == nil && resp.StatusCode == http.StatusBadRequest {
// 400 'invalid session token': the token failed local verification.
// Re-run the domain's auth flow and replace the stored token; do not retry unchanged.
} Prevention
- Store session tokens verbatim; never re-type or trim parts of them.
- Re-issue tokens after any management session-key change or domain re-registration.
- Send each token only to the domain it was issued for.
- Log the 400 distinctly from 401: 400 means bad token, 401 means valid shape but denied.
When it happens
Trigger: Calling the domain with the scheme's header (e.g. Authorization or X-Api-Token per the header scheme) carrying a token that is not a valid session token signed by the domain's SessionPublicKey: garbage string, token signed for a different domain/key, truncated or base64-mangled value, or an expired-token parse error from the local validator.
Common situations: Script or API client with a copy-pasted token that picked up whitespace/newlines; tokens issued before a session key rotation (AddDomain re-registered with a new public key); sending an IdP access token where a proxy session token is expected; key mismatch between management and proxy after reconfiguration.
Related errors
- no daemon pipe to connect to
- engine not started
- tun module not available
- sync response is not available
- host argument required
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/247a5dbf0cd2f188.
Report an issue: GitHub.