{"record":{"id":"139ceb53b0a35991","repo":"caddyserver/caddy","slug":"app-module-s-w","errorCode":null,"errorMessage":"app module %s: %w","messagePattern":"app module (.+?): %w","errorType":"validation","errorClass":"ErrNotConfigured","httpStatus":null,"severity":"error","filePath":"context.go","lineNumber":532,"sourceCode":"\tmodVal, err := ctx.LoadModuleByID(name, appRaw)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"loading %s app module: %v\", name, err)\n\t}\n\tif appRaw != nil {\n\t\tctx.cfg.AppsRaw[name] = nil // allow GC to deallocate\n\t}\n\treturn modVal, nil\n}\n\n// AppIfConfigured is like App, but it returns an error if the\n// app has not been configured. This is useful when the app is\n// required and its absence is a configuration error; or when\n// the app is optional and you don't want to instantiate a\n// new one that hasn't been explicitly configured. If the app\n// is not in the configuration, the error wraps ErrNotConfigured.\nfunc (ctx Context) AppIfConfigured(name string) (any, error) {\n\tif ctx.cfg == nil {\n\t\treturn nil, fmt.Errorf(\"app module %s: %w\", name, ErrNotConfigured)\n\t}\n\t// if the app failed to load before, return the cached error\n\tif err, ok := ctx.cfg.failedApps[name]; ok {\n\t\treturn nil, fmt.Errorf(\"loading %s app module: %v\", name, err)\n\t}\n\tif app, ok := ctx.cfg.apps[name]; ok {\n\t\treturn app, nil\n\t}\n\tappRaw := ctx.cfg.AppsRaw[name]\n\tif appRaw == nil {\n\t\treturn nil, fmt.Errorf(\"app module %s: %w\", name, ErrNotConfigured)\n\t}\n\treturn ctx.App(name)\n}\n\n// ErrNotConfigured indicates a module is not configured.\nvar ErrNotConfigured = fmt.Errorf(\"module not configured\")\n","sourceCodeStart":514,"sourceCodeEnd":550,"githubUrl":"https://github.com/caddyserver/caddy/blob/50e54ee279aa1e504fe218ca49ab6ae16c100410/context.go#L514-L550","documentation":"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.","triggerScenarios":"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.","commonSituations":"Unit tests or embedding code creating caddy.Context{} directly instead of via cfg.NewContext; calling app-dependent helpers before a config is loaded.","solutions":["Obtain the Context from a loaded config: cfg := &caddy.Config{}; ctx := cfg.NewContext(caddy.Context{}) — never use a zero-value Context for app access","Check errors.Is(err, caddy.ErrNotConfigured) to treat absence as optional rather than fatal","In tests, build a minimal Config with the needed AppsRaw/apps entries"],"exampleFix":"// before\nctx := caddy.Context{} // no cfg\napp, err := ctx.AppIfConfigured(\"tls\")\n// after\ncfg := &caddy.Config{}\nctx := cfg.NewContext(caddy.Context{})\napp, err := ctx.AppIfConfigured(\"tls\")","handlingStrategy":"validation","validationCode":"// Never call AppIfConfigured on a context without a config\ncfg := &caddy.Config{ AppsRaw: caddy.ModuleMap{ \"tls\": json.RawMessage(`{}`) } }\nctx := cfg.NewContext(caddy.Context{})\nif _, err := ctx.AppIfConfigured(\"tls\"); err != nil { /* handle */ }","typeGuard":"func contextHasConfig(ctx caddy.Context) bool { return ctx != caddy.Context{} /* prefer constructing contexts only from real configs */ }","tryCatchPattern":"if _, err := ctx.AppIfConfigured(name); err != nil {\n    if errors.Is(err, caddy.ErrNotConfigured) { /* absent: use defaults */ } else { return err }\n}","preventionTips":["Always create Context via cfg.NewContext, never caddy.Context{}","Treat ErrNotConfigured as an expected, handled branch","In tests, build a minimal Config with the apps you need"],"tags":["caddy","apps","context","not-configured"],"backgroundTag":null,"analyzedSha":"50e54ee279aa1e504fe218ca49ab6ae16c100410","analyzedAt":"2026-08-15T09:20:21.641Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}