netbirdio/netbird · error

invalid anonymize level %q: use %q or %q

Error message

invalid anonymize level %q: use %q or %q

What it means

effectiveAnonymize (client/cmd/root.go:301-310) validates --anonymize-level by a round-trip: anonymize.ParseLevel maps unknown strings to LevelStrict (client/anonymize/anonymize.go:44), so the CLI compares the input (case-insensitively) to the parsed level's String(); a mismatch means the value was not 'default' or 'strict'. Only those two levels exist: LevelDefault redacts public IPs/domains/MACs; LevelStrict additionally redacts internal ranges, peer names, and netbird keys.

Source

Thrown at client/cmd/root.go:307

var CLIBackOffSettings = &backoff.ExponentialBackOff{
	InitialInterval:     time.Second,
	RandomizationFactor: backoff.DefaultRandomizationFactor,
	Multiplier:          backoff.DefaultMultiplier,
	MaxInterval:         10 * time.Second,
	MaxElapsedTime:      30 * time.Second,
	Stop:                backoff.Stop,
	Clock:               backoff.SystemClock,
}

// effectiveAnonymize resolves the --anonymize and --anonymize-level flags:
// setting a level implies anonymization, and an invalid level is rejected.
func effectiveAnonymize() (bool, anonymize.Level, error) {
	if anonymizeLevelFlag == "" {
		return anonymizeFlag, anonymize.LevelDefault, nil
	}
	level := anonymize.ParseLevel(anonymizeLevelFlag)
	if !strings.EqualFold(anonymizeLevelFlag, level.String()) {
		return false, anonymize.LevelDefault, fmt.Errorf("invalid anonymize level %q: use %q or %q", anonymizeLevelFlag, anonymize.LevelDefault.String(), anonymize.LevelStrict.String())
	}
	return true, level, nil
}

func getSetupKey() (string, error) {
	if setupKeyPath != "" && setupKey == "" {
		return getSetupKeyFromFile(setupKeyPath)
	}
	return setupKey, nil
}

func getSetupKeyFromFile(setupKeyPath string) (string, error) {
	data, err := os.ReadFile(setupKeyPath)
	if err != nil {
		return "", fmt.Errorf("failed to read setup key file: %v", err)
	}
	return strings.TrimSpace(string(data)), nil
}

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Use exactly `--anonymize-level default` or `--anonymize-level strict` (case-insensitive)
  2. If full redaction is wanted, that is 'strict'; if only public IPs/domains/MACs, that is 'default'
  3. Drop the flag entirely — anonymization then defaults off, or use plain --anonymize (default level)
  4. Check NB_ANONYMIZE_LEVEL in the environment and correct or unset it

Example fix

# before
netbird debug daemon-logs --anonymize-level medium
# -> invalid anonymize level "medium": use "default" or "strict"
# after
netbird debug daemon-logs --anonymize-level strict
Defensive patterns

Strategy: validation

Validate before calling

level := strings.ToLower(strings.TrimSpace(anonymizeLevelFlag))
if level != "" && level != "default" && level != "strict" {
    return fmt.Errorf("invalid anonymize level %q: use \"default\" or \"strict\"", anonymizeLevelFlag)
}

Type guard

func isValidAnonymizeLevel(s string) bool {
    s = strings.ToLower(strings.TrimSpace(s))
    return s == "default" || s == "strict"
}

Prevention

When it happens

Trigger: --anonymize-level set to anything except 'default' or 'strict' (e.g. 'medium', 'full', 'high', 'anonymize'); NB_ANONYMIZE_LEVEL env var with a typo; scripts written for another tool's level names; trailing whitespace in the value (flag parsing does not trim).

Common situations: Users assuming a graded scale (low/medium/high); copy-paste from docs of a different product version; env var typo like NB_ANONYMIZE_LVL spelled out elsewhere and set here; CI setting the env globally for unrelated reasons.

Related errors


AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16). Data as JSON: /api/errors/98b532b94eb32aac. Report an issue: GitHub.