gostructor

Known limitations & error handling

Known limitations

Error handling

Every error Configure returns belongs to one of a small, closed set of categories, so you can tell exactly what went wrong — and whose fault it is — without string-matching. Match the sentinels with errors.Is and the struct types with errors.As; each struct type unwraps to its underlying cause.

Error When Whose problem
ErrInvalidTarget (sentinel) target isn’t a non-nil pointer to a struct the calling code
*NotResolvedError (wraps ErrFieldNotResolved) a configured field (not optional) produced no value from any source a missing env var / file key / secret
*SourceError a source failed: file missing or malformed, Vault unreachable, … the backing store
*ConvertError a value was produced but doesn’t fit the field’s type (fractional float into int, overflow, bad duration) the value in the config
*HookError a WithHook callback rejected the value or returned the wrong type your validation/transform

Every field-scoped error (NotResolvedError, SourceError, ConvertError, HookError) implements the FieldError interface, so you can recover which field failed without switching on the concrete type:

type FieldError interface {
    error
    FieldName() string
}
cfg, err := gostructor.Configure(&Config{})
switch {
case err == nil:
    // ok

case errors.Is(err, gostructor.ErrFieldNotResolved):
    var nre *gostructor.NotResolvedError
    errors.As(err, &nre)
    log.Fatalf("no value for %s (tried %s)", nre.Field, strings.Join(nre.Sources, ", "))

case func() bool { var se *gostructor.SourceError; return errors.As(err, &se) }():
    var se *gostructor.SourceError
    errors.As(err, &se)
    log.Fatalf("source %s failed for %s: %v", se.Source, se.Field, se.Unwrap())

default:
    var ce *gostructor.ConvertError
    if errors.As(err, &ce) {
        // ce.Field, ce.Value, ce.Target describe exactly what didn't fit,
        // and errors.As can reach the underlying *strconv.NumError etc.
        log.Fatalf("field %s: bad value %#v for %s", ce.Field, ce.Value, ce.Target)
    }
    log.Fatal(err)
}