caddyserver/caddy · critical
field %s does not exist in %#v
Error message
field %s does not exist in %#v
What it means
Context.LoadModule uses reflection to fetch the named struct field and panics if it does not exist. This is an API-contract error: LoadModule is only meant to be called by a module on its own struct fields (those with a caddy struct tag), typically from within its own Provision method. A wrong fieldName or wrong struct pointer is a programming bug, so it panics rather than returning an error.
Source
Thrown at context.go:194
//
// This will look for a key/value pair like `"handler": "..."` in the json.RawMessage
// in order to know the module name.
//
// To make use of the loaded module(s) (the return value), you will probably want
// to type-assert each 'any' value(s) to the types that are useful to you
// and store them on the same struct. Storing them on the same struct makes for
// easy garbage collection when your host module is no longer needed.
//
// Loaded modules have already been provisioned and validated. Upon returning
// successfully, this method clears the json.RawMessage(s) in the field since
// the raw JSON is no longer needed, and this allows the GC to free up memory.
func (ctx Context) LoadModule(structPointer any, fieldName string) (any, error) {
val := reflect.ValueOf(structPointer).Elem().FieldByName(fieldName)
typ := val.Type()
field, ok := reflect.TypeOf(structPointer).Elem().FieldByName(fieldName)
if !ok {
panic(fmt.Sprintf("field %s does not exist in %#v", fieldName, structPointer))
}
opts, err := ParseStructTag(field.Tag.Get("caddy"))
if err != nil {
panic(fmt.Sprintf("malformed tag on field %s: %v", fieldName, err))
}
moduleNamespace, ok := opts["namespace"]
if !ok {
panic(fmt.Sprintf("missing 'namespace' key in struct tag on field %s", fieldName))
}
inlineModuleKey := opts["inline_key"]
var result any
switch val.Kind() {
case reflect.Slice:
if isJSONRawMessage(typ) {View on GitHub (pinned to 50e54ee279)
Solutions
- Make the fieldName string exactly match the exported struct field that carries the caddy:"namespace=..." tag
- Call ctx.LoadModule only from within that module's own Provision(ctx) on itself
- Compile-time alternative: keep the string adjacent to the field definition and cover it with a unit test that provisions the module
Example fix
// before
type H struct {
UpstreamRaw json.RawMessage `caddy:"namespace=http.reverse_proxy.upstreams"`
}
func (h *H) Provision(ctx caddy.Context) error {
val, err := ctx.LoadModule(h, "Upstream") // wrong name -> panic
}
// after
func (h *H) Provision(ctx caddy.Context) error {
val, err := ctx.LoadModule(h, "UpstreamRaw")
if err != nil { return err }
h.Upstream = val.(Upstream)
return nil
} Defensive patterns
Strategy: type-guard
Validate before calling
if _, ok := reflect.TypeOf(m).Elem().FieldByName(fieldName); !ok {
return fmt.Errorf("field %s missing on %T — fix LoadModule call", fieldName, m)
} Type guard
func hasCaddyField(ptr any, fieldName string) bool {
f, ok := reflect.TypeOf(ptr).Elem().FieldByName(fieldName)
return ok && f.Tag.Get("caddy") != ""
}
// use before loading:
if !hasCaddyField(&h, "UpstreamRaw") {
panic("programmer error: bad field name for LoadModule")
} Try / catch
defer func() {
if r := recover(); r != nil {
if strings.HasPrefix(fmt.Sprint(r), "field ") && strings.Contains(fmt.Sprint(r), "does not exist") {
log.Fatalf("LoadModule called with wrong fieldName: %v", r)
}
panic(r)
}
}()
val, err := ctx.LoadModule(&h, "UpstreamRaw") Prevention
- Only call ctx.LoadModule from the module's own Provision on its own tagged fields
- Keep the fieldName string next to the field and cover with tests that run provisioning
When it happens
Trigger: Calling ctx.LoadModule(m, "Wrap") when the field is actually named WrappedRaw; passing a non-pointer or a pointer to a different struct; passing the host module's pointer plus a field that only exists on the embedded module type.
Common situations: Plugin development with renamed struct fields without updating the Provision body; typos in the fieldName string; calling LoadModule outside Provision where the receiver is not the module being loaded.
Related errors
- position %d: %v
- unrecognized type for module: %s
- module is not a request matcher: %T
- command short string is required
- command already registered: %s
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/4d432e29544772d5.
Report an issue: GitHub.