{"id":"4b1fbabc3ec271aa","repo":"spf13/viper","slug":"failed-to-read-configuration-from-input-w","errorCode":null,"errorMessage":"failed to read configuration from input: %w","messagePattern":"failed to read configuration from input: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"viper.go","lineNumber":1790,"sourceCode":"\tdefer f.Close()\n\n\tif err := v.marshalWriter(f, configType); err != nil {\n\t\treturn err\n\t}\n\n\treturn f.Sync()\n}\n\nfunc (v *Viper) unmarshalReader(in io.Reader, c map[string]any) error {\n\tformat := strings.ToLower(v.getConfigType())\n\tif format == \"\" {\n\t\treturn errors.New(\"cannot decode configuration: unable to determine config type\")\n\t}\n\n\tbuf := new(bytes.Buffer)\n\t_, err := buf.ReadFrom(in)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to read configuration from input: %w\", err)\n\t}\n\n\t// TODO: remove this once SupportedExts is deprecated/removed\n\tif !slices.Contains(SupportedExts, format) {\n\t\treturn UnsupportedConfigError(format)\n\t}\n\n\t// TODO: return [UnsupportedConfigError] if the registry does not contain the format\n\t// TODO: consider deprecating this error type\n\tdecoder, err := v.decoderRegistry.Decoder(format)\n\tif err != nil {\n\t\treturn ConfigParseError{err}\n\t}\n\n\terr = decoder.Decode(buf.Bytes(), c)\n\tif err != nil {\n\t\treturn ConfigParseError{err}\n\t}","sourceCodeStart":1772,"sourceCodeEnd":1808,"githubUrl":"https://github.com/spf13/viper/blob/528f7416c4b56a4948673984b190bf8713f0c3c4/viper.go#L1772-L1808","documentation":"Returned by unmarshalReader (viper.go:1787-1791) wrapping the error from buf.ReadFrom(in). It fires when the io.Reader itself fails to produce bytes — the format was resolved fine, but the read I/O errored. The underlying cause is preserved via %w for errors.Is/errors.As inspection.","triggerScenarios":"ReadConfig with a *os.File that was already Closed; reading from a remote key-value provider (consul/etcd via viper/remote) whose Get returns a broken or short reader; reading from a pipe/network reader that errors mid-stream; bytes/strings readers essentially never trigger this.","commonSituations":"defer f.Close() placed before ReadConfig runs (or Close called explicitly too early); transient network failure reading remote config; corrupted/garbage stream from a misbehaving provider; permission denied re-opening on a WatchConfig reload.","solutions":["Inspect the wrapped error: if errors.Is(err, io.ErrClosedPipe) / os.ErrClosed -> the reader was closed.","Ensure the reader stays open until ReadConfig returns (close with defer after the read, not before).","For remote config, add retry with backoff around the ReadConfig call and verify the provider connection."],"exampleFix":"// before\nf, _ := os.Open(\"config.yaml\")\nf.Close()\n_ = v.ReadConfig(f) // -> failed to read configuration from input: read ...: file already closed\n\n// after\nf, err := os.Open(\"config.yaml\")\nif err != nil { return err }\ndefer f.Close()\nif err := v.ReadConfig(f); err != nil {\n    var pathErr *fs.PathError\n    if errors.As(err, &pathErr) { /* handle I/O */ }\n}","handlingStrategy":"try-catch","validationCode":"// Ensure the reader is usable before handing it to Viper.\nfunc safeOpenConfig(path string) (io.ReadCloser, error) {\n    f, err := os.Open(path)\n    if err != nil { return nil, err }\n    // probe a single byte to catch already-closed/permission issues early\n    buf := make([]byte, 1)\n    if _, err := f.Read(buf); err != nil && err != io.EOF {\n        f.Close()\n        return nil, err\n    }\n    if _, err := f.Seek(0, io.SeekStart); err != nil { f.Close(); return nil, err }\n    return f, nil\n}","typeGuard":null,"tryCatchPattern":"if err := v.ReadConfig(r); err != nil {\n    var wrapped *wrappedErr // surface inner via errors.Is/errors.As on the %w chain\n    if errors.Is(err, os.ErrClosed) {\n        // reader was closed before ReadConfig\n    } else if errors.Is(err, io.ErrUnexpectedEOF) {\n        // truncated stream\n    }\n}","preventionTips":["Close readers with defer AFTER ReadConfig returns, not before.","For remote config, wrap ReadConfig in retry-with-backoff and surface provider errors.","Always inspect the wrapped error (%w) rather than matching the top-level string."],"tags":["config-read","io","remote","wrapped-error"],"analyzedSha":"528f7416c4b56a4948673984b190bf8713f0c3c4","analyzedAt":"2026-08-04T21:46:12.352Z","schemaVersion":2}