diff --git a/config/registry.go b/config/registry.go index 01a7d72..e0128ad 100644 --- a/config/registry.go +++ b/config/registry.go @@ -104,29 +104,43 @@ func Register(option *Option) error { return fmt.Errorf("config: invalid default value: %w", vErr) } - if err := loadUnmappedValue(option); err != nil { - return err + hasUnmappedValue, vErr := loadUnmappedValue(option) + if vErr != nil && !vErr.SoftError { + return fmt.Errorf("config: invalid value: %w", vErr) } optionsLock.Lock() defer optionsLock.Unlock() options[option.Key] = option - return nil + if hasUnmappedValue { + signalChanges() + } + + // return the validation-error from loadUnmappedValue here + return vErr } -func loadUnmappedValue(option *Option) error { +func loadUnmappedValue(option *Option) (bool, *ValidationError) { unmappedValuesLock.Lock() defer unmappedValuesLock.Unlock() + if value, ok := unmappedValues[option.Key]; ok { delete(unmappedValues, option.Key) var vErr *ValidationError option.activeValue, vErr = validateValue(option, value) if vErr != nil { - return fmt.Errorf("config: invalid value: %w", vErr) + // we consider this error as a "soft" error so lazily registered + // options don't fail the hard way. + option.activeValue = option.activeFallbackValue + vErr.SoftError = true + + return true, vErr } + + return true, nil } - return nil + return false, nil } diff --git a/config/validate.go b/config/validate.go index 4120d2e..67d7352 100644 --- a/config/validate.go +++ b/config/validate.go @@ -205,8 +205,9 @@ func validateValue(option *Option, value interface{}) (*valueCache, *ValidationE // ValidationError error holds details about a config option value validation error. type ValidationError struct { - Option *Option - Err error + Option *Option + Err error + SoftError bool } // Error returns the formatted error.