caddyserver/caddy · error

key %s: %v

Error message

key %s: %v

What it means

loadModulesFromRegularMap iterates a map[string]json.RawMessage field where map keys are arbitrary (not module names); each value must be an inline module object carrying its module name under the inline key (e.g. "handler"). If loading any entry fails, the error is prefixed with the offending map key. The %v carries the underlying cause from loadModuleInline.

Source

Thrown at context.go:326

	}

	// otherwise, val is a map with modules, but the module name is
	// inline with each value (the key means something else)
	return ctx.loadModulesFromRegularMap(namespace, inlineModuleKey, val)
}

// loadModulesFromRegularMap loads modules from val, where val is a map[string]json.RawMessage.
// Map keys are NOT interpreted as module names, so module names are still expected to appear
// inline with the objects.
func (ctx Context) loadModulesFromRegularMap(namespace, inlineModuleKey string, val reflect.Value) (map[string]any, error) {
	mods := make(map[string]any)
	iter := val.MapRange()
	for iter.Next() {
		k := iter.Key()
		v := iter.Value()
		mod, err := ctx.loadModuleInline(inlineModuleKey, namespace, v.Interface().(json.RawMessage))
		if err != nil {
			return nil, fmt.Errorf("key %s: %v", k, err)
		}
		mods[k.String()] = mod
	}
	return mods, nil
}

// loadModuleMap loads modules from a ModuleMap, i.e. map[string]any, where the key is the
// module name. With a module map, module names do not need to be defined inline with their values.
func (ctx Context) loadModuleMap(namespace string, val reflect.Value) (map[string]any, error) {
	all := make(map[string]any)
	iter := val.MapRange()
	for iter.Next() {
		k := iter.Key().Interface().(string)
		v := iter.Value().Interface().(json.RawMessage)
		moduleName := namespace + "." + k
		if namespace == "" {
			moduleName = k
		}

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Find the map entry named by the key in the error message within the module's JSON section
  2. Ensure that entry is a JSON object containing the inline module-name key (check the module's docs for the key name, e.g. "handler") and a registered module ID as its value
  3. Fix the underlying cause shown after 'key <name>:' — usually 'module name not specified' or 'unknown module'
  4. Validate the whole config with `caddy validate --config <file>` after the edit

Example fix

// before
"dns": { "cloudflare": null }
// after
"dns": { "cloudflare": { "name": "cloudflare", "api_token": "{env.CF_TOKEN}" } }
Defensive patterns

Strategy: validation

Validate before calling

// Pre-check every map value is an object with the inline module key
func validateInlineMap(raw map[string]json.RawMessage, inlineKey string) error {
    for k, v := range raw {
        var obj map[string]json.RawMessage
        if err := json.Unmarshal(v, &obj); err != nil {
            return fmt.Errorf("key %s: value must be an object", k)
        }
        if _, ok := obj[inlineKey]; !ok {
            return fmt.Errorf("key %s: missing inline module key %q", k, inlineKey)
        }
    }
    return nil
}

Type guard

func isInlineModuleObject(raw json.RawMessage) bool {
    var obj map[string]json.RawMessage
    return json.Unmarshal(raw, &obj) == nil && obj != nil
}

Try / catch

if _, err := ctx.LoadModule(field, raw); err != nil { return fmt.Errorf("module map: %w", err) } // 'key K:' names the entry

Prevention

When it happens

Trigger: A module struct field typed map[string]json.RawMessage with a caddy module tag and an inline_key; one map value is null, a scalar, an array, or an object missing/wrongly-naming the inline module key, causing loadModuleInline to fail and the loop to return fmt.Errorf("key %s: %v", k, err).

Common situations: JSON config for modules like dns providers or matchers keyed by custom names where one entry's object lacks the module-name key; renaming the inline key between module versions; setting a map entry to null.

Related errors


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