caddyserver/caddy · critical
unable to determine module name without inline_key when type
Error message
unable to determine module name without inline_key when type is not a ModuleMap
What it means
When a field is declared as json.RawMessage (a single raw JSON blob), LoadModule must find the module name inline inside the JSON, which requires the tag to declare inline_key. With no inline_key and a non-ModuleMap type, Caddy has no way to know which module to instantiate, so it panics. This is an API-contract violation between the field's type and its struct tag.
Source
Thrown at context.go:216
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) {
// val is `json.RawMessage` ([]uint8 under the hood)
if inlineModuleKey == "" {
panic("unable to determine module name without inline_key when type is not a ModuleMap")
}
val, err := ctx.loadModuleInline(inlineModuleKey, moduleNamespace, val.Interface().(json.RawMessage))
if err != nil {
return nil, err
}
result = val
} else if isJSONRawMessage(typ.Elem()) {
// val is `[]json.RawMessage`
if inlineModuleKey == "" {
panic("unable to determine module name without inline_key because type is not a ModuleMap")
}
var all []any
for i := 0; i < val.Len(); i++ {
val, err := ctx.loadModuleInline(inlineModuleKey, moduleNamespace, val.Index(i).Interface().(json.RawMessage))
if err != nil {
return nil, fmt.Errorf("position %d: %v", i, err)
}View on GitHub (pinned to 50e54ee279)
Solutions
- Add inline_key to the tag: `caddy:"namespace=http.matchers inline_key=matcher"`, where the JSON object must then contain that key naming the module
- Or change the field type to caddy.ModuleMap if the map key should be the module name
- Ensure the JSON config actually includes the inline_key field with a valid module name
Example fix
// before
type Route struct {
Matcher json.RawMessage `json:"matcher,omitempty" caddy:"namespace=http.matchers"`
}
// after
type Route struct {
Matcher json.RawMessage `json:"matcher,omitempty" caddy:"namespace=http.matchers inline_key=matcher"`
} Defensive patterns
Strategy: validation
Validate before calling
import "reflect"
func requireInlineKey(ptr any, fieldName string) error {
f, _ := reflect.TypeOf(ptr).Elem().FieldByName(fieldName)
t := f.Type
raw := t == reflect.TypeOf(json.RawMessage{})
if raw {
opts, _ := caddy.ParseStructTag(f.Tag.Get("caddy"))
if opts["inline_key"] == "" {
return fmt.Errorf("%s is json.RawMessage; its caddy tag needs inline_key", fieldName)
}
}
return nil
} Try / catch
defer func() { if r := recover(); r != nil { err = fmt.Errorf("LoadModule: %v", r) } }() Prevention
- json.RawMessage fields always need inline_key in the tag
- Prefer caddy.ModuleMap when keys should name modules
- Unit-test config unmarshal + provision for each new field
When it happens
Trigger: Declaring a field as json.RawMessage with a tag like `caddy:"namespace=http.handlers"` (no inline_key) and calling ctx.LoadModule on it.
Common situations: Custom modules that want flexible inline module specs (like Caddyfile-matched matchers); porting a ModuleMap field to json.RawMessage without updating the tag; following an example that omitted inline_key.
Related errors
- malformed tag on field %s: %v
- missing 'namespace' key in struct tag on field %s
- unable to determine module name without inline_key because t
- expected ModuleMap because inline_key is empty; but we do no
- module ID missing
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/d6c98fa3d764e020.
Report an issue: GitHub.