{"record":{"id":"035f8918c8e950fb","repo":"netbirdio/netbird","slug":"one-of-setupkey-jwttoken-or-privatekey-must-be-p","errorCode":null,"errorMessage":"one of SetupKey, JWTToken, or PrivateKey must be provided","messagePattern":"one of SetupKey, JWTToken, or PrivateKey must be provided","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/embed/embed.go","lineNumber":140,"sourceCode":"\t// only; ignored by Client.SetPerformance.\n\tMaxBatchSize *uint32\n}\n\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}","sourceCodeStart":122,"sourceCodeEnd":158,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/embed/embed.go#L122-L158","documentation":"embed.Options.validateCredentials requires exactly one registration credential for the embedded client (client/embed): SetupKey, JWTToken, or PrivateKey. This is the zero-credential case — the counter never incremented, so New() aborts before creating the client. The embedded client has no interactive SSO fallback, hence the hard requirement.","triggerScenarios":"Calling embed.New(embed.Options{...}) without setting any of the three credential fields, e.g. intending interactive login which the embedded build does not support.","commonSituations":"Porting CLI-style usage (where `netbird up` can prompt for SSO) into the embedded library, or forgetting to wire the credential from the environment into Options.","solutions":["Provide a setup key: opts.SetupKey = \"AAAA...\"","Or provide a JWT token obtained out-of-band: opts.JWTToken = token","Or provide the peer private key for re-registration: opts.PrivateKey = key"],"exampleFix":"// before\nclient, err := embed.New(embed.Options{ManagementURL: \"https://mgr\"})  // err: one of SetupKey, JWTToken, or PrivateKey must be provided\n// after\nclient, err := embed.New(embed.Options{\n\tManagementURL: \"https://mgr\",\n\tSetupKey:      os.Getenv(\"NETBIRD_SETUP_KEY\"),\n})","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"func hasExactlyOneCredential(o embed.Options) error {\n\tn := 0\n\tfor _, c := range []string{o.SetupKey, o.JWTToken, o.PrivateKey} {\n\t\tif c != \"\" {\n\t\t\tn++\n\t\t}\n\t}\n\tif n != 1 {\n\t\treturn fmt.Errorf(\"got %d credentials, want exactly 1\", n)\n\t}\n\treturn nil\n}","tryCatchPattern":"if _, err := embed.New(opts); err != nil {\n\tif strings.Contains(err.Error(), \"must be provided\") {\n\t\t// credential wiring bug, not transient: fetch a setup key and rebuild opts\n\t}\n}","preventionTips":["Fail fast in config loading if no credential source is configured","Remember the embedded client has no interactive SSO; always provision a credential"],"tags":["go","netbird","embedded","validation","authentication","setup-key"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}