caddyserver/caddy · critical

unable to determine module name without inline_key because t

Error message

unable to determine module name without inline_key because type is not a ModuleMap

What it means

Same contract as for a single json.RawMessage, but the field is a []json.RawMessage (slice of raw module configs). Each element needs an inline module name, so the tag must declare inline_key; without it LoadModule panics because it cannot derive module names. This is a compile-time-visible design mistake in the module struct.

Source

Thrown at context.go:227

	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)
				}
				all = append(all, val)
			}
			result = all
		} else if typ.Elem().Kind() == reflect.Slice && isJSONRawMessage(typ.Elem().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++ {

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Add inline_key to the struct tag so each JSON element carries its module name
  2. Or switch the field type to caddy.ModuleMap / caddy.ModuleMap-based types when map keys should name modules
  3. Validate that every element of the JSON array contains the inline_key property

Example fix

// before
type Route struct {
    Matchers []json.RawMessage `json:"match,omitempty" caddy:"namespace=http.matchers"`
}
// after
type Route struct {
    Matchers []json.RawMessage `json:"match,omitempty" caddy:"namespace=http.matchers inline_key=matcher"`
}
Defensive patterns

Strategy: validation

Validate before calling

func requireSliceInlineKey(ptr any, fieldName string) error {
    f, _ := reflect.TypeOf(ptr).Elem().FieldByName(fieldName)
    if f.Type.Kind() == reflect.Slice && f.Type.Elem() == reflect.TypeOf(json.RawMessage{}) {
        opts, _ := caddy.ParseStructTag(f.Tag.Get("caddy"))
        if opts["inline_key"] == "" {
            return fmt.Errorf("%s is []json.RawMessage; needs inline_key", fieldName)
        }
    }
    return nil
}

Try / catch

defer func() { if r := recover(); r != nil { err = fmt.Errorf("LoadModule: %v", r) } }()

Prevention

When it happens

Trigger: Declaring a field as []json.RawMessage whose tag lacks inline_key, e.g. `caddy:"namespace=http.matchers"`, then calling ctx.LoadModule.

Common situations: Custom modules holding a list of matcher/handler blobs; converting a ModuleMap list to a raw-message slice for ordering guarantees while keeping the old tag.

Related errors


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