Skip to content

Zero-value Viper struct panics on SetDefault/Set (nil map assignment) #2111

@quocvibui

Description

@quocvibui

Summary

Creating a Viper instance via struct literal (&viper.Viper{}) instead of viper.New() causes a nil map panic on the first call to SetDefault() or Set(). The internal maps (defaults, override, etc.) are not initialized, leading to assignment to entry in nil map.

While viper.New() is the documented constructor, the Viper struct type is exported, so users can construct zero-value instances. A nil-map guard or lazy initialization would make the API more robust.

Reproduction

package main

import "github.com/spf13/viper"

func main() {
    v := &viper.Viper{} // zero-value, not viper.New()
    v.SetDefault("key", "value")
    // panic: assignment to entry in nil map
}

Both SetDefault and Set panic the same way:

goroutine 1 [running]:
github.com/spf13/viper.(*Viper).SetDefault(...)
    viper.go:XXX

viper.New() works correctly — it initializes all internal maps.

Why this matters

  1. Exported type contract: Since Viper is an exported struct, users may reasonably construct it directly. Go convention (e.g., http.Client{}, sync.Mutex{}) is that zero-value exported types should be usable or at minimum not panic.
  2. Debugging difficulty: The assignment to entry in nil map panic gives no hint that the user should have used New() instead of &Viper{}.
  3. Blast radius: Viper is used by 30k+ projects including Cobra, Hugo, and many CLI tools. A confusing panic during configuration setup wastes developer time.

Suggested Fix

Add nil-map guards at the top of SetDefault and Set:

func (v *Viper) SetDefault(key string, value interface{}) {
    if v.defaults == nil {
        v.defaults = make(map[string]interface{})
    }
    // ... existing logic
}

Or alternatively, add a lazyInit() method that ensures all maps are initialized, called at the top of each mutating method.

Environment

  • viper v1.19.0
  • Go 1.25.1 darwin/arm64
  • Panic confirmed on both SetDefault and Set

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions