spf13/viper · error
cannot decode configuration: unable to determine config type
Error message
cannot decode configuration: unable to determine config type
What it means
Returned by unmarshalReader (viper.go:1781-1785) when getConfigType() returns "". getConfigType returns "" if v.configType is unset and either getConfigFile errors (no file located) or the resolved filename has no extension (viper.go:2121-2137). Without a format, Viper cannot pick a decoder.
Source
Thrown at viper.go:1784
flags |= os.O_EXCL
}
f, err := v.fs.OpenFile(filename, flags, v.configPermissions)
if err != nil {
return err
}
defer f.Close()
if err := v.marshalWriter(f, configType); err != nil {
return err
}
return f.Sync()
}
func (v *Viper) unmarshalReader(in io.Reader, c map[string]any) error {
format := strings.ToLower(v.getConfigType())
if format == "" {
return errors.New("cannot decode configuration: unable to determine config type")
}
buf := new(bytes.Buffer)
_, err := buf.ReadFrom(in)
if err != nil {
return fmt.Errorf("failed to read configuration from input: %w", err)
}
// TODO: remove this once SupportedExts is deprecated/removed
if !slices.Contains(SupportedExts, format) {
return UnsupportedConfigError(format)
}
// TODO: return [UnsupportedConfigError] if the registry does not contain the format
// TODO: consider deprecating this error type
decoder, err := v.decoderRegistry.Decoder(format)
if err != nil {
return ConfigParseError{err}View on GitHub (pinned to 528f7416c4)
Solutions
- Call v.SetConfigType("yaml") (or json/toml/env) before v.ReadConfig(reader).
- If reading from disk, ensure SetConfigName + AddConfigPath resolve a file with a recognized extension so getConfigType can infer it.
- For remote providers, call SetConfigType after AddRemoteProvider.
Example fix
// before
v := New()
_ = v.ReadConfig(strings.NewReader(`{"port": 8080}`)) // -> cannot decode configuration: unable to determine config type
// after
v := New()
v.SetConfigType("json")
_ = v.ReadConfig(strings.NewReader(`{"port": 8080}`)) Defensive patterns
Strategy: validation
Validate before calling
// getConfigType() is unexported; mirror its logic to validate before reading.
func hasConfigType(v *viper.Viper, readerOnly bool) bool {
if v.GetConfigType() != "" { return true }
if readerOnly { return false } // no filename to infer from
// rely on SetConfigName + AddConfigPath resolving an extensioned file
return false
}
// before ReadConfig(reader):
if v.GetConfigType() == "" {
v.SetConfigType("yaml") // or json/toml/env
}
v.ReadConfig(reader) Try / catch
if err := v.ReadConfig(r); err != nil {
if strings.Contains(err.Error(), "unable to determine config type") {
v.SetConfigType("yaml")
err = v.ReadConfig(r)
}
} Prevention
- Always pair ReadConfig/ReadConfigAs with an explicit SetConfigType.
- For remote/env/reader sources where no filename exists, treat SetConfigType as mandatory.
- Wrap Viper setup in a helper that refuses to return a Viper with empty config type for reader-based apps.
When it happens
Trigger: Calling v.ReadConfig(reader) or v.ReadConfigAs(reader) without first calling v.SetConfigType(...). Also when WatchConfig fires on a Viper instance whose config file has no recognized extension and SetConfigType was never called, or when reading remote/env config with no type set.
Common situations: Reading config from an io.Reader (string, bytes, env blob, remote KV) where there is no filename to infer the type from; refactoring that removed the SetConfigType call; reading a file literally named 'config' with no extension.
Related errors
- decoder not found for this format
- missing configuration for 'configPath'
- config type could not be determined for %s
- failed to read configuration from input: %w
AI-assisted analysis of spf13/viper@528f7416c4 (2026-08-04).
Data as JSON: /data/errors/1cf13ac4b7d973d0.json.
Report an issue: GitHub.