netbirdio/netbird · warning

unknown log level: %s. Available levels are: panic, fatal, e

Error message

unknown log level: %s. Available levels are: panic, fatal, error, warn, info, debug, trace

What it means

setLogLevel parses the requested level with server.ParseLogLevel, which lowercases the input and maps only panic, fatal, error, warn, info, debug and trace; anything else returns LogLevel_UNKNOWN and this error. The check happens client-side before any RPC, so the daemon was not contacted. Note the mapping is lowercase-insensitive but strictly named — 'warning', 'err', 'verbose', 'none', or an empty string all fail.

Source

Thrown at client/cmd/debug.go:218

	return nil
}

func setLogLevel(cmd *cobra.Command, args []string) error {
	conn, err := getClient(cmd)
	if err != nil {
		return err
	}
	defer func() {
		if err := conn.Close(); err != nil {
			log.Errorf(errCloseConnection, err)
		}
	}()

	client := proto.NewDaemonServiceClient(conn)
	level := server.ParseLogLevel(args[0])
	if level == proto.LogLevel_UNKNOWN {
		//nolint
		return fmt.Errorf("unknown log level: %s. Available levels are: panic, fatal, error, warn, info, debug, trace\n", args[0])
	}

	_, err = client.SetLogLevel(cmd.Context(), &proto.SetLogLevelRequest{
		Level: level,
	})
	if err != nil {
		return fmt.Errorf("failed to set log level: %v", status.Convert(err).Message())
	}

	cmd.Println("Log level set successfully to", args[0])
	return nil
}

func runForDuration(cmd *cobra.Command, args []string) error {
	duration, err := time.ParseDuration(args[0])
	if err != nil {
		return fmt.Errorf("invalid duration format: %v", err)
	}

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Use one of the exact names from the message: panic, fatal, error, warn, info, debug, trace (case-insensitive)
  2. For 'warning' type 'warn'; for 'err' type 'error'; for 'off/all' there is no equivalent — choose trace (most verbose) or a higher level
  3. Quote the argument exactly once and confirm it is non-empty: netbird debug log-level 'debug'

Example fix

# before
netbird debug log-level warning
# -> unknown log level: warning. ...

# after
netbird debug log-level warn
Defensive patterns

Strategy: validation

Validate before calling

// Reuse the daemon's parser so validation matches exactly:
if server.ParseLogLevel(arg) == proto.LogLevel_UNKNOWN {
    return fmt.Errorf("unknown log level: %s", arg)
}

Type guard

// Go set membership for the accepted vocabulary:
func isValidLogLevel(s string) bool {
    switch strings.ToLower(s) {
    case "panic", "fatal", "error", "warn", "info", "debug", "trace":
        return true
    }
    return false
}

Prevention

When it happens

Trigger: netbird debug log-level warning (the canonical name is warn); netbird debug log-level err or ERROR_LEVEL; passing a logrus/zap level name that is not in the list (trace is supported, 'fatal' is, 'panic' is, but 'all'/'off' are not); quoting issues leaving an empty argument.

Common situations: Users assuming the log levels of the underlying logging library carry over (logrus uses warn but people type warning; some tools use notice/emerg); scripts parameterized from a config that stores a different level vocabulary; muscle memory from netbird up --log-level? — the accepted set is identical, but typos still trip it.

Related errors


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