{"record":{"id":"bf09d46168e11ecb","repo":"netbirdio/netbird","slug":"parse-log-level-w","errorCode":null,"errorMessage":"parse log level: %w","messagePattern":"parse log level: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/embed/embed.go","lineNumber":168,"sourceCode":"func 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}\n\t}\n\n\tif opts.LogOutput != nil {\n\t\tlogrus.SetOutput(opts.LogOutput)\n\t}\n\n\tif opts.LogLevel != \"\" {\n\t\tlevel, err := logrus.ParseLevel(opts.LogLevel)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"parse log level: %w\", err)\n\t\t}\n\t\tlogrus.SetLevel(level)\n\t}\n\n\tif !opts.NoUserspace {\n\t\tif err := os.Setenv(netstack.EnvUseNetstackMode, \"true\"); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"setenv: %w\", err)\n\t\t}\n\t\tif err := os.Setenv(netstack.EnvSkipProxy, \"true\"); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"setenv: %w\", err)\n\t\t}\n\t}\n\n\tif opts.StatePath != \"\" {\n\t\t// TODO: Disable state if path not provided\n\t\tif err := os.Setenv(\"NB_DNS_STATE_FILE\", opts.StatePath); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"setenv: %w\", err)\n\t\t}","sourceCodeStart":150,"sourceCodeEnd":186,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/embed/embed.go#L150-L186","documentation":"Returned by embed.New when Options.LogLevel is non-empty and logrus.ParseLevel rejects it. Valid levels are panic, fatal, error, warn, warning, info, debug and trace (case-insensitive). The embedded client reuses the process-global logrus logger, so the level must be a valid logrus level string.","triggerScenarios":"Calling embed.New with LogLevel set to values like \"verbose\", \"err\", \"WARN \" (trailing space), \"INFO\" works but \"informational\", or an empty-ish string like \" \".","commonSituations":"Passing a level name from a different logging library (zap: \"dpanic\"; slog: \"WARN\" is fine but slog numeric levels are not); reading the level from an env var that is unset-but-not-empty; typo or case mistakes are tolerated but any unknown word fails.","solutions":["Use one of: panic, fatal, error, warn, warning, info, debug, trace.","Validate with logrus.ParseLevel before calling embed.New when the string comes from user input or config files.","Leave LogLevel empty to keep the default (info)."],"exampleFix":"// before\nclient, err := embed.New(embed.Options{LogLevel: cfg.LogLevel})\n\n// after\nif _, perr := logrus.ParseLevel(cfg.LogLevel); cfg.LogLevel != \"\" && perr != nil {\n    return fmt.Errorf(\"bad log level %q: %w\", cfg.LogLevel, perr)\n}\nclient, err := embed.New(embed.Options{LogLevel: cfg.LogLevel})","handlingStrategy":"validation","validationCode":"import \"github.com/sirupsen/logrus\"\n\nif opts.LogLevel != \"\" {\n    if _, err := logrus.ParseLevel(opts.LogLevel); err != nil {\n        return fmt.Errorf(\"invalid log level %q: %w\", opts.LogLevel, err)\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Define an allowlist of level constants in your own config schema instead of free-form strings.","Validate config at load time, not at embed.New time."],"tags":["config","logging","validation","embed"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}