Preflight Checklist
Problem Description
WithDecodeHook (and the DecodeHook decoder option) replace viper's default hook chain rather than extend it. The default chain is built in defaultDecoderConfig:
decodeHook = mapstructure.ComposeDecodeHookFunc(
mapstructure.StringToTimeDurationHookFunc(),
stringToWeakSliceHookFunc(","),
)
stringToWeakSliceHookFunc is unexported, so any application that adds a single custom hook silently loses the weak string-to-slice behaviour and cannot restore it — the only workaround is to copy the function into the application.
The difference is observable: decoding "a,b" into []string yields ["a", "b"] with viper's default chain and ["a,b"] without it (with WeaklyTypedInput).
Proposed Solution
Either:
-
Export the default chain, e.g. viper.DefaultDecodeHook() returning the composed hook (and/or export StringToWeakSliceHookFunc), so callers can compose:
viper.WithDecodeHook(mapstructure.ComposeDecodeHookFunc(
myHook,
viper.DefaultDecodeHook(),
))
-
Or add an option that appends to the defaults instead of replacing them, e.g. viper.WithAdditionalDecodeHook(h).
Option 1 is the smaller change and keeps composition explicit.
Alternatives Considered
Copying stringToWeakSliceHookFunc into the application — works, but duplicates viper internals and drifts when the default chain changes (the source already carries a TODO about replacing that implementation).
Additional Information
Viper v1.21.0. Encountered while adding an ${env:NAME} resolution hook to an application's config decoding, where the rest of viper's default decoding behaviour must stay intact.
🤖 Filed with Claude Code
Preflight Checklist
Problem Description
WithDecodeHook(and theDecodeHookdecoder option) replace viper's default hook chain rather than extend it. The default chain is built indefaultDecoderConfig:stringToWeakSliceHookFuncis unexported, so any application that adds a single custom hook silently loses the weak string-to-slice behaviour and cannot restore it — the only workaround is to copy the function into the application.The difference is observable: decoding
"a,b"into[]stringyields["a", "b"]with viper's default chain and["a,b"]without it (withWeaklyTypedInput).Proposed Solution
Either:
Export the default chain, e.g.
viper.DefaultDecodeHook()returning the composed hook (and/or exportStringToWeakSliceHookFunc), so callers can compose:Or add an option that appends to the defaults instead of replacing them, e.g.
viper.WithAdditionalDecodeHook(h).Option 1 is the smaller change and keeps composition explicit.
Alternatives Considered
Copying
stringToWeakSliceHookFuncinto the application — works, but duplicates viper internals and drifts when the default chain changes (the source already carries a TODO about replacing that implementation).Additional Information
Viper v1.21.0. Encountered while adding an
${env:NAME}resolution hook to an application's config decoding, where the rest of viper's default decoding behaviour must stay intact.🤖 Filed with Claude Code