Preflight Checklist

  • [X] I have searched the issue tracker for an issue that matches the one I want to file, without success.
  • [X] I am not looking for support or already pursued the available support channels without success.
  • [X] I have checked the troubleshooting guide for my problem, without success.

Viper Version

v1.15.0

Go Version

go1.18.7

Config Source

Environment variables, Files

Format

YAML

Repl.it link

https://replit.com/@vt128/Viper-bind-bug#repro.sh

Code reproducing the issue

Here's the code to repro the issue:

Struct to hold the config:


type Module struct {
    Enabled bool
    Target  uint
    Name    string
}

type ConfigSchema struct {
    Module Module
}


The corresponding config file:

```yaml
token: "local"
module:
  enabled: 1
  target: 42
  name: yes

module.target: 52

The code to read the config:

// Set config path
rp, err := os.UserHomeDir()
if err != nil {
    log.Fatalw("Failed to get user home directory", "error", err)
}

viper.AddConfigPath(".")
viper.AddConfigPath(rp)
viper.SetConfigType("yaml")
viper.SetConfigName("setting")

// Set env
viper.AutomaticEnv()
replacer := strings.NewReplacer("-", "_")
viper.SetEnvKeyReplacer(replacer)
viper.SetEnvPrefix("app")
viper.BindEnv("token", "TEST")
err = viper.BindEnv("module.target", "TARGET", "MOD")
if err != nil {
    log.Fatalw("Failed to bind env", "error", err)
}

// for default value
viper.SetDefault("Module", Module{
    Enabled: true,
    Target:  100,
    Name:    "default",
})

// Read config file
var C ConfigSchema
if err := viper.ReadInConfig(); err != nil {
    log.Fatalw("Failed to read config", "error", err)
}

// Parse config
if err := viper.Unmarshal(&C); err != nil {
    log.Fatalw("Failed to unmarshal config", "error", err)
}

all := viper.AllSettings()

Expected Behavior

Run TARGET=8080 ./main, C.Module.Target and all.module.target are always 8080 consistently

Actual Behavior

I got the following inconsistent results:

  • 40-50% of the time, the C.Module.Target is 42, not 8080 from the env
  • While C.Module.Target is 42, all.module.target may be 8080 or 42, the possibility is 50-50
  • For the rest, the bind env works, and C.Module.Target and all.module.target are 8080

If the viper.SetDefault("Module", ...) is removed, the bind env works consistently, i.e. C.Module.Target and all.module.target were always 8080.

Steps To Reproduce

The command to run for 1000 times:

for x in $(seq 1 1000); do TARGET=8080 ./main; done

In Repl.it link -- just run the ./repro.sh, it repeats for 100 times.

Additional Information

No response

Comment From: github-actions[bot]

👋 Thanks for reporting!

A maintainer will take a look at your issue shortly. 👀

In the meantime: We are working on Viper v2 and we would love to hear your thoughts about what you like or don't like about Viper, so we can improve or fix those issues.

⏰ If you have a couple minutes, please take some time and share your thoughts: https://forms.gle/R6faU74qPRPAzchZ9

📣 If you've already given us your feedback, you can still help by spreading the news, either by sharing the above link or telling people about this on Twitter:

https://twitter.com/sagikazarmark/status/1306904078967074816

Thank you! ❤️

Comment From: github-actions[bot]

Issues with no activity for 30 days are marked stale and subject to being closed.