{"record":{"id":"247a5dbf0cd2f188","repo":"netbirdio/netbird","slug":"client-already-started","errorCode":null,"errorMessage":"client already started","messagePattern":"client already started","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"client/embed/embed.go","lineNumber":32,"sourceCode":"\t\"github.com/sirupsen/logrus\"\n\twgdevice \"golang.zx2c4.com/wireguard/device\"\n\twgnetstack \"golang.zx2c4.com/wireguard/tun/netstack\"\n\n\t\"github.com/netbirdio/netbird/client/iface\"\n\t\"github.com/netbirdio/netbird/client/iface/netstack\"\n\t\"github.com/netbirdio/netbird/client/internal\"\n\t\"github.com/netbirdio/netbird/client/internal/auth\"\n\t\"github.com/netbirdio/netbird/client/internal/peer\"\n\t\"github.com/netbirdio/netbird/client/internal/profilemanager\"\n\tsshcommon \"github.com/netbirdio/netbird/client/ssh\"\n\t\"github.com/netbirdio/netbird/client/system\"\n\t\"github.com/netbirdio/netbird/shared/management/domain\"\n\tmgmProto \"github.com/netbirdio/netbird/shared/management/proto\"\n\t\"github.com/netbirdio/netbird/util/capture\"\n)\n\nvar (\n\tErrClientAlreadyStarted = errors.New(\"client already started\")\n\tErrClientNotStarted     = errors.New(\"client not started\")\n\tErrEngineNotStarted     = errors.New(\"engine not started\")\n\tErrConfigNotInitialized = errors.New(\"config not initialized\")\n)\n\nconst (\n\t// PeerStatusConnected indicates the peer is in connected state.\n\tPeerStatusConnected = peer.StatusConnected\n)\n\n// PeerConnStatus is a peer's connection status.\ntype PeerConnStatus = peer.ConnStatus\n\n// Client manages a netbird embedded client instance.\ntype Client struct {\n\tdeviceName string\n\tconfig     *profilemanager.Config\n\tmu         sync.Mutex","sourceCodeStart":14,"sourceCodeEnd":50,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/embed/embed.go#L14-L50","documentation":"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'.","triggerScenarios":"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.","commonSituations":"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.","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'."],"exampleFix":"# before: stale/mangled token\ncurl -H 'X-Api-Token: abc123…truncated' https://app.example.com/\n# -> 400 invalid session token\n\n# after: freshly issued token, sent verbatim\ncurl -H \"X-Api-Token: $NB_SESSION_TOKEN\" https://app.example.com/","handlingStrategy":"validation","validationCode":"// Before sending: cheap sanity check that the token is the expected shape\n// (opaque session token, no stray whitespace/newlines).\nfunc looksLikeSessionToken(tok string) bool {\n    tok = strings.TrimSpace(tok)\n    return tok != \"\" && !strings.ContainsAny(tok, \" \\t\\r\\n\")\n}","typeGuard":null,"tryCatchPattern":"resp, err := client.Do(req)\nif err == nil && resp.StatusCode == http.StatusBadRequest {\n    // 400 'invalid session token': the token failed local verification.\n    // Re-run the domain's auth flow and replace the stored token; do not retry unchanged.\n}","preventionTips":["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."],"tags":["proxy","session-token","ed25519","authentication"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}