goconfigurationenvironment-variablesunmarshallingviper

How to make Viper Unmarshal return an error if an env var is not set in Golang?


I'm using Viper in Golang to load configuration values from environment variables. However, I want viper.Unmarshal to return an error if a required environment variable is not set.

By default, if an environment variable is missing, viper.Unmarshal does not fail—it simply assigns the zero value to the struct field.

Here's my code:

package main

import (
    "fmt"
    "log"

    "github.com/spf13/viper"
)

type Config struct {
    DatabaseURL string `mapstructure:"DATABASE_URL"`
}

func main() {
    viper.AutomaticEnv()

    var config Config
    if err := viper.Unmarshal(&config); err != nil {
        log.Fatalf("Error unmarshaling config: %v", err)
    }

    fmt.Println("Config:", config)
}

If DATABASE_URL is not set, config.DatabaseURL is just an empty string instead of causing an error.

I tried using viper.BindEnv("DATABASE_URL"), but Unmarshal still does not fail when DATABASE_URL is missing.

In Viper, the Unmarshal function accepts hooks through DecoderConfigOption. Looking into viper, I found that the DecoderConfig struct has an ErrorUnset field:

// If ErrorUnset is true, then it is an error for there to exist  
// fields in the result that were not set in the decoding process  
// (extra fields). This only applies to decoding to a struct. This  
// will affect all nested structs as well.  
ErrorUnset bool 

However, I’m not sure how to pass this configuration properly as a hook in Unmarshal. If anyone knows how to enable ErrorUnset using DecoderConfigOption, thank you!

How can I make viper.Unmarshal DecodeHook return an error if a required environment variable is not set?


Solution

  • I'm not sure if my answer is as described in your question, but you can refer to it.You can use the following code. I found that when we set ErrorUnset=true, we will check if we have set the required environment variables during unmarshal.

    package main
    
    import (
        "fmt"
        "log"
    
        "github.com/go-viper/mapstructure/v2"
        "github.com/spf13/viper"
    )
    
    type Config struct {
        DatabaseURL string `mapstructure:"DATABASE_URL"`
    }
    
    func main() {
        viper.AutomaticEnv()
        var config Config
        if err := viper.Unmarshal(&config, func(dc *mapstructure.DecoderConfig) {
            dc.ErrorUnset = true // Set ErrorUnset field to true here
        }); err != nil {
            log.Fatalf("Error unmarshaling config: %v", err)
        }
        fmt.Println("Config:", config)
    }
    // output 
    2025/03/31 10:38:09 Error unmarshaling config: decoding failed due to the following error(s):
    
    '' has unset fields: DATABASE_URL
    exit status 1