{"record":{"id":"15345385457b19f3","repo":"uber-go/zap","slug":"missing-level","errorCode":null,"errorMessage":"missing Level","messagePattern":"missing Level","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"config.go","lineNumber":251,"sourceCode":"\t\tOutputPaths:      []string{\"stderr\"},\n\t\tErrorOutputPaths: []string{\"stderr\"},\n\t}\n}\n\n// Build constructs a logger from the Config and Options.\nfunc (cfg Config) Build(opts ...Option) (*Logger, error) {\n\tenc, err := cfg.buildEncoder()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tsink, errSink, err := cfg.openSinks()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif cfg.Level == (AtomicLevel{}) {\n\t\treturn nil, errors.New(\"missing Level\")\n\t}\n\n\tlog := New(\n\t\tzapcore.NewCore(enc, sink, cfg.Level),\n\t\tcfg.buildOptions(errSink)...,\n\t)\n\tif len(opts) > 0 {\n\t\tlog = log.WithOptions(opts...)\n\t}\n\treturn log, nil\n}\n\nfunc (cfg Config) buildOptions(errSink zapcore.WriteSyncer) []Option {\n\topts := []Option{ErrorOutput(errSink)}\n\n\tif cfg.Development {\n\t\topts = append(opts, Development())\n\t}","sourceCodeStart":233,"sourceCodeEnd":269,"githubUrl":"https://github.com/uber-go/zap/blob/bbd4ecbd8760678fdbac94c93d9aca8069c83b2f/config.go#L233-L269","documentation":"Config.Build validates that a non-zero Level is configured before constructing the logger. Because AtomicLevel's zero value is not usable, zap rejects configurations where cfg.Level was never set, returning errors.New(\"missing Level\"). This prevents silently building a logger with an uninitialized level.","triggerScenarios":"Calling cfg.Build() (or zap.New(...)-style construction via Config) on a zap.Config literal where the Level field was left as the zero-value AtomicLevel{} — e.g. zap.Config{Encoding:\"json\"} without cfg.Level = zap.NewAtomicLevel().","commonSituations":"Hand-rolled zap.Config structs instead of zap.NewProductionConfig()/NewDevelopmentConfig; copying a config but forgetting to re-set Level; dynamic config loading that omits the level key.","solutions":["Set cfg.Level = zap.NewAtomicLevel() (or zap.NewAtomicLevelAt(zapcore.InfoLevel)) before calling Build","Start from zap.NewProductionConfig() or zap.NewDevelopmentConfig() and override fields instead of building a zero-value Config","If using an AtomicLevel shared with dynamic config, ensure it is initialized via zap.NewAtomicLevel before Build"],"exampleFix":"// before\ncfg := zap.Config{Encoding: \"json\", OutputPaths: []string{\"stderr\"}}\nlogger, err := cfg.Build()\n// after\ncfg := zap.Config{Encoding: \"json\", OutputPaths: []string{\"stderr\"}}\ncfg.Level = zap.NewAtomicLevelAt(zapcore.InfoLevel)\nlogger, err := cfg.Build()","handlingStrategy":"validation","validationCode":"if cfg.Level == (zap.AtomicLevel{}) {\n    cfg.Level = zap.NewAtomicLevelAt(zapcore.InfoLevel)\n}\nlogger, err := cfg.Build()","typeGuard":"func hasLevel(c zap.Config) bool {\n    return c.Level != (zap.AtomicLevel{})\n}","tryCatchPattern":null,"preventionTips":["Always start from zap.NewProductionConfig()/NewDevelopmentConfig() and mutate","Set cfg.Level immediately when constructing any zap.Config literal","Use zap.New(core, opts) directly when you need full control instead of zero-value Config"],"tags":["config","zap","go","initialization"],"backgroundTag":"missing-required-config-field","analyzedSha":"bbd4ecbd8760678fdbac94c93d9aca8069c83b2f","analyzedAt":"2026-08-31T13:01:40.228Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}