Skip to content

Latest commit

 

History

History
117 lines (84 loc) · 3.15 KB

File metadata and controls

117 lines (84 loc) · 3.15 KB

Errors

goxdi uses sentinel errors. Branch with errors.Is — do not compare error strings.

import "errors"

_, err := goxdi.Get[*Repo](scope)
if errors.Is(err, goxdi.ErrNotRegistered) {
	// …
}

Sentinel catalog

Sentinel Typical cause
ErrNotRegistered Resolving a type that was never Add*-ed
ErrAlreadyRegistered Second Add* for the same T on one builder
ErrScopeRequired Resolving a scoped type from the root container
ErrClosed Get / Close after the scope or root was closed
ErrScopeClosed Alias of ErrClosed (kept for compatibility)
ErrCircularDependency Factory cycle detected during creation

ErrNotRegistered

Surfaces at resolve time, including when a factory asks for a missing dependency.

_ = goxdi.AddSingleton(b, func(r goxdi.Resolver) (*Repo, error) {
	db, err := goxdi.Get[*DB](r) // not registered
	if err != nil {
		return nil, err
	}
	return &Repo{DB: db}, nil
})

_, err := goxdi.Get[*Repo](root)
// errors.Is(err, goxdi.ErrNotRegistered)

Prefer failing boot by resolving the app entrypoint once before serving.

Avoid treating “not registered” as a normal control-flow signal in hot paths unless optional deps are a deliberate design.

ErrAlreadyRegistered

Returned by Add* when T already exists. The original registration is unchanged.

err := goxdi.AddSingleton(b, secondFactory)
if errors.Is(err, goxdi.ErrAlreadyRegistered) {
	// keep first factory; do not assume override
}

ErrScopeRequired

Means “this type needs a unit-of-work boundary”.

_, err := goxdi.Get[*UnitOfWork](root)
if errors.Is(err, goxdi.ErrScopeRequired) {
	scope := root.NewScope()
	defer scope.Close()
	uow, err := goxdi.Get[*UnitOfWork](scope)
	_ = uow
	_ = err
}

ErrClosed (ErrScopeClosed)

Returned when using a closed scope/root, including a second Close. Prefer ErrClosed; ErrScopeClosed is the same sentinel.

Prefer:

scope := root.NewScope()
defer scope.Close()
// resolve only while scope is open

Avoid storing a scope on a long-lived struct and resolving after the request finished.

ErrCircularDependency

The error wraps the sentinel and includes a type path. Break the cycle in your model; see Resolving.

Factory errors

If a factory returns a custom error, Get returns it as-is (it is not rewritten into a sentinel).

_ = goxdi.AddSingleton(b, func(goxdi.Resolver) (*DB, error) {
	return nil, fmt.Errorf("open db: %w", errOpen)
})

You may wrap sentinels yourself if you need more context:

db, err := goxdi.Get[*DB](r)
if err != nil {
	return nil, fmt.Errorf("repo: %w", err)
}

MustGet and panics

MustGet panics with the same error value Get would return. That panic is recoverable with recover, but recovering DI failures usually hides misconfiguration — prefer Get at recoverable boundaries.

Next steps