netbirdio/netbird · error
only one of SetupKey, JWTToken, or PrivateKey can be specifi
Error message
only one of SetupKey, JWTToken, or PrivateKey can be specified
What it means
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.
Source
Thrown at client/embed/embed.go:143
// validateCredentials checks that exactly one credential type is provided
func (opts *Options) validateCredentials() error {
credentialsProvided := 0
if opts.SetupKey != "" {
credentialsProvided++
}
if opts.JWTToken != "" {
credentialsProvided++
}
if opts.PrivateKey != "" {
credentialsProvided++
}
if credentialsProvided == 0 {
return fmt.Errorf("one of SetupKey, JWTToken, or PrivateKey must be provided")
}
if credentialsProvided > 1 {
return fmt.Errorf("only one of SetupKey, JWTToken, or PrivateKey can be specified")
}
return nil
}
// New creates a new netbird embedded client.
func New(opts Options) (*Client, error) {
if err := opts.validateCredentials(); err != nil {
return nil, err
}
if opts.MTU != nil {
if err := iface.ValidateMTU(*opts.MTU); err != nil {
return nil, fmt.Errorf("invalid MTU: %w", err)
}
}
if opts.LogOutput != nil {View on GitHub (pinned to 93e97f4bf1)
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
Example fix
// before
client, err := embed.New(embed.Options{SetupKey: sk, PrivateKey: pk}) // err: only one of ...
// after
client, err := embed.New(embed.Options{PrivateKey: pk}) // exactly one credential Defensive patterns
Strategy: validation
Validate before calling
// enforce single-credential precedence before New()
switch {
case o.SetupKey != "":
o.JWTToken, o.PrivateKey = "", ""
case o.JWTToken != "":
o.PrivateKey = ""
} Prevention
- 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
When it happens
Trigger: embed.New with both SetupKey and JWTToken set (common when falling back between env vars), or PrivateKey combined with either of the others.
Common situations: 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.
Related errors
- one of SetupKey, JWTToken, or PrivateKey must be provided
- connector type change not allowed
- service name is required
- service name exceeds maximum length of 255 characters
- at least one target is required
AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16).
Data as JSON: /api/errors/c65270f02218f92f.
Report an issue: GitHub.