caddyserver/caddy · error · ErrNotConfigured

app module %s: %w

Error message

app module %s: %w

What it means

AppIfConfigured is like App but errors with ErrNotConfigured when the app is absent. This branch fires when ctx.cfg itself is nil — i.e. the Context has no configuration at all — returning 'app module %s: %w' wrapping ErrNotConfigured. A nil cfg means the context cannot possibly have the app, so it is reported as not configured.

Source

Thrown at context.go:532

	modVal, err := ctx.LoadModuleByID(name, appRaw)
	if err != nil {
		return nil, fmt.Errorf("loading %s app module: %v", name, err)
	}
	if appRaw != nil {
		ctx.cfg.AppsRaw[name] = nil // allow GC to deallocate
	}
	return modVal, nil
}

// AppIfConfigured is like App, but it returns an error if the
// app has not been configured. This is useful when the app is
// required and its absence is a configuration error; or when
// the app is optional and you don't want to instantiate a
// new one that hasn't been explicitly configured. If the app
// is not in the configuration, the error wraps ErrNotConfigured.
func (ctx Context) AppIfConfigured(name string) (any, error) {
	if ctx.cfg == nil {
		return nil, fmt.Errorf("app module %s: %w", name, ErrNotConfigured)
	}
	// if the app failed to load before, return the cached error
	if err, ok := ctx.cfg.failedApps[name]; ok {
		return nil, fmt.Errorf("loading %s app module: %v", name, err)
	}
	if app, ok := ctx.cfg.apps[name]; ok {
		return app, nil
	}
	appRaw := ctx.cfg.AppsRaw[name]
	if appRaw == nil {
		return nil, fmt.Errorf("app module %s: %w", name, ErrNotConfigured)
	}
	return ctx.App(name)
}

// ErrNotConfigured indicates a module is not configured.
var ErrNotConfigured = fmt.Errorf("module not configured")

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Obtain the Context from a loaded config: cfg := &caddy.Config{}; ctx := cfg.NewContext(caddy.Context{}) — never use a zero-value Context for app access
  2. Check errors.Is(err, caddy.ErrNotConfigured) to treat absence as optional rather than fatal
  3. In tests, build a minimal Config with the needed AppsRaw/apps entries

Example fix

// before
ctx := caddy.Context{} // no cfg
app, err := ctx.AppIfConfigured("tls")
// after
cfg := &caddy.Config{}
ctx := cfg.NewContext(caddy.Context{})
app, err := ctx.AppIfConfigured("tls")
Defensive patterns

Strategy: validation

Validate before calling

// Never call AppIfConfigured on a context without a config
cfg := &caddy.Config{ AppsRaw: caddy.ModuleMap{ "tls": json.RawMessage(`{}`) } }
ctx := cfg.NewContext(caddy.Context{})
if _, err := ctx.AppIfConfigured("tls"); err != nil { /* handle */ }

Type guard

func contextHasConfig(ctx caddy.Context) bool { return ctx != caddy.Context{} /* prefer constructing contexts only from real configs */ }

Try / catch

if _, err := ctx.AppIfConfigured(name); err != nil {
    if errors.Is(err, caddy.ErrNotConfigured) { /* absent: use defaults */ } else { return err }
}

Prevention

When it happens

Trigger: Calling ctx.AppIfConfigured(name) on a Context constructed without a config (e.g. zero-value Context or one created for tests/tooling), hitting the ctx.cfg == nil guard and wrapping ErrNotConfigured with the app name.

Common situations: Unit tests or embedding code creating caddy.Context{} directly instead of via cfg.NewContext; calling app-dependent helpers before a config is loaded.

Related errors


AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15). Data as JSON: /api/errors/139ceb53b0a35991. Report an issue: GitHub.