caddyserver/caddy · error

config missing, unable to create dev logger: %v

Error message

config missing, unable to create dev logger: %v

What it means

Context.Logger falls back to a zap development logger when the context has no config (typical in unit tests). If even zap.NewDevelopment() fails — nearly always because stderr is unavailable or zap's internal setup errors — the function panics with this message. It is rare and environmental rather than a config mistake.

Source

Thrown at context.go:581

// However, that is no longer necessary, as the closest module
// most recently associated with the context will be automatically
// assumed. To prevent a sudden breaking change, this method's
// signature has been changed to be variadic, but we may remove
// the parameter altogether in the future. Callers should not
// pass in any argument. If there is valid need to specify a
// different module, please open an issue to discuss.
//
// PARTIALLY DEPRECATED: The Logger(module) form is deprecated and
// may be removed in the future. Do not pass in any arguments.
func (ctx Context) Logger(module ...Module) *zap.Logger {
	if len(module) > 1 {
		panic("more than 1 module passed in")
	}
	if ctx.cfg == nil {
		// often the case in tests; just use a dev logger
		l, err := zap.NewDevelopment()
		if err != nil {
			panic("config missing, unable to create dev logger: " + err.Error())
		}
		return l
	}
	mod := ctx.Module()
	if len(module) > 0 {
		mod = module[0]
	}
	if mod == nil {
		return Log()
	}
	return ctx.cfg.Logging.Logger(mod)
}

type slogHandlerFactory func(handler slog.Handler, core zapcore.Core, moduleID string) slog.Handler

var (
	slogHandlerFactories   []slogHandlerFactory
	slogHandlerFactoriesMu sync.RWMutex

View on GitHub (pinned to 50e54ee279)

Solutions

  1. In tests, build the Context via caddy.Context{ctx: context.Background()} with a real config, or use caddy.Log() instead
  2. Ensure the process has a working stderr (do not close os.Stderr; check sandbox/seccomp rules)
  3. Provision modules with caddy.New(caddy.Config{...}) style flows so ctx.cfg is set before logging

Example fix

// before
ctx := caddy.Context{} // cfg == nil
logger := ctx.Logger()
// after
logger := caddy.Log() // or provision via caddy.New() so the context carries a config
Defensive patterns

Strategy: validation

Validate before calling

func safeLogger(ctx caddy.Context) *zap.Logger {
    if ctx.cfg == nil { // no config: use the global logger instead of the dev-logger fallback
        return caddy.Log()
    }
    return ctx.Logger()
}

Prevention

When it happens

Trigger: Calling ctx.Logger() on a Context constructed as caddy.Context{} (cfg == nil, common in tests) on a system where zap.NewDevelopment returns an error, e.g. closed/invalid stderr or restrictive sandbox.

Common situations: Unit tests that build bare Context values; running Caddy-derived code in sandboxes (seccomp, WASM) where os.Stderr writes fail; embedding Caddy modules in another application without provisioning.

Related errors


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