{"record":{"id":"c65270f02218f92f","repo":"netbirdio/netbird","slug":"only-one-of-setupkey-jwttoken-or-privatekey-can","errorCode":null,"errorMessage":"only one of SetupKey, JWTToken, or PrivateKey can be specified","messagePattern":"only one of SetupKey, JWTToken, or PrivateKey can be specified","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/embed/embed.go","lineNumber":143,"sourceCode":"\n// validateCredentials checks that exactly one credential type is provided\nfunc (opts *Options) validateCredentials() error {\n\tcredentialsProvided := 0\n\tif opts.SetupKey != \"\" {\n\t\tcredentialsProvided++\n\t}\n\tif opts.JWTToken != \"\" {\n\t\tcredentialsProvided++\n\t}\n\tif opts.PrivateKey != \"\" {\n\t\tcredentialsProvided++\n\t}\n\n\tif credentialsProvided == 0 {\n\t\treturn fmt.Errorf(\"one of SetupKey, JWTToken, or PrivateKey must be provided\")\n\t}\n\tif credentialsProvided > 1 {\n\t\treturn fmt.Errorf(\"only one of SetupKey, JWTToken, or PrivateKey can be specified\")\n\t}\n\n\treturn nil\n}\n\n// New creates a new netbird embedded client.\nfunc New(opts Options) (*Client, error) {\n\tif err := opts.validateCredentials(); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif opts.MTU != nil {\n\t\tif err := iface.ValidateMTU(*opts.MTU); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"invalid MTU: %w\", err)\n\t\t}\n\t}\n\n\tif opts.LogOutput != nil {","sourceCodeStart":125,"sourceCodeEnd":161,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/embed/embed.go#L125-L161","documentation":"The mirror case of 438 in embed.Options.validateCredentials: more than one of SetupKey, JWTToken, or PrivateKey was set, making the intended registration credential ambiguous. New() refuses to guess and aborts. Exactly one must be present.","triggerScenarios":"embed.New with both SetupKey and JWTToken set (common when falling back between env vars), or PrivateKey combined with either of the others.","commonSituations":"Config loaders that set every credential field they find (e.g. setup key from env plus a cached private key from state), or defensive 'set everything' initialization.","solutions":["Set exactly one credential field and leave the others empty","In config loaders, apply precedence explicitly (e.g. SetupKey wins, so clear JWTToken/PrivateKey) instead of populating all","When reusing persisted state with PrivateKey, drop the setup key from options"],"exampleFix":"// before\nclient, err := embed.New(embed.Options{SetupKey: sk, PrivateKey: pk})  // err: only one of ...\n// after\nclient, err := embed.New(embed.Options{PrivateKey: pk})  // exactly one credential","handlingStrategy":"validation","validationCode":"// enforce single-credential precedence before New()\nswitch {\ncase o.SetupKey != \"\":\n\to.JWTToken, o.PrivateKey = \"\", \"\"\ncase o.JWTToken != \"\":\n\to.PrivateKey = \"\"\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Pick one credential source per deployment mode and zero the others explicitly","When restoring state with PrivateKey, ensure setup keys from env are not also copied into Options"],"tags":["go","netbird","embedded","validation","authentication","credential-selection"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}