netbirdio/netbird · error

init log: %w

Error message

init log: %w

What it means

Returned from the `netbird ssh` PersistentPreRun when util.InitLog fails to initialize the logger with the requested level and output. InitLog parses the --log-level flag value and opens/configures the log output (console or the first existing log file from --log-file); an unrecognized level or an unusable output path makes it return an error that is wrapped here.

Source

Thrown at client/cmd/ssh.go:149

func sshFn(cmd *cobra.Command, args []string) error {
	for _, arg := range args {
		if arg == "-h" || arg == "--help" {
			return cmd.Help()
		}
	}

	SetFlagsFromEnvVars(rootCmd)
	SetFlagsFromEnvVars(cmd)

	cmd.SetOut(cmd.OutOrStdout())

	logOutput := "console"
	if firstLogFile := util.FindFirstLogPath(logFiles); firstLogFile != "" && firstLogFile != defaultLogFile {
		logOutput = firstLogFile
	}
	if err := util.InitLog(logLevel, logOutput); err != nil {
		return fmt.Errorf("init log: %w", err)
	}

	ctx := internal.CtxInitState(cmd.Context())

	sig := make(chan os.Signal, 1)
	signal.Notify(sig, syscall.SIGTERM, syscall.SIGINT)
	sshctx, cancel := context.WithCancel(ctx)

	errCh := make(chan error, 1)
	go func() {
		if err := runSSH(sshctx, host, cmd); err != nil {
			errCh <- err
		}
		cancel()
	}()

	select {
	case <-sig:

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Re-run with a supported --log-level value (the standard set: panic/fatal/error/warn/info/debug/trace — e.g., --log-level info).
  2. Check the shell variable feeding --log-level; empty or misspelled values fail, unset the flag to use the default.
  3. If you also pass --log-file, point it at a path your user can write, or drop the flag to log to console.
  4. Verify with `netbird ssh --log-level debug <host>` that the rest of the command proceeds past logging setup.

Example fix

# before
LOG_LVL="verbose" netbird ssh --log-level "$LOG_LVL" peer1
# -> init log: invalid log level

# after
netbird ssh --log-level debug peer1
Defensive patterns

Strategy: validation

Validate before calling

// validate level + output before the CLI call
var validLevels = map[string]bool{"panic":true,"fatal":true,"error":true,"warn":true,"info":true,"debug":true,"trace":true}
if lvl := os.Getenv("NB_SSH_LOG_LEVEL"); lvl != "" && !validLevels[lvl] {
	log.Fatalf("unsupported --log-level %q", lvl)
}
if lf := os.Getenv("NB_SSH_LOG_FILE"); lf != "" {
	if _, err := os.Stat(filepath.Dir(lf)); err != nil {
		log.Fatalf("log dir missing: %v", err)
	}
}

Type guard

func isValidLogLevel(s string) bool {
	switch s {
	case "panic", "fatal", "error", "warn", "info", "debug", "trace":
		return true
	}
	return false
}

Try / catch

if err := util.InitLog(logLevel, logOutput); err != nil {
	// classify: unrecognized level vs unwritable sink; the first is a flag typo,
	// the second a filesystem/permission issue — print the offending value
	fmt.Fprintf(os.Stderr, "logging init failed for level=%q output=%q: %v\n", logLevel, logOutput, err)
	return err
}

Prevention

When it happens

Trigger: Running `netbird ssh --log-level <bad>` with a value outside the accepted set (e.g., verbose, high, trace2), or passing --log-file entries whose first resolvable path exists but cannot be opened/appended by the current user. The ssh command runs as a regular user, so daemon-owned log files under the system log dir can trigger permission failures.

Common situations: Copying a --log-level value from older NetBird docs or other tools that use different level names; scripts that pass --log-level=$LOG_LEVEL with an empty or misspelled variable; running ssh with a log file path that only root can write.

Related errors


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