caddyserver/caddy · error

unknown module: %s

Error message

unknown module: %s

What it means

LoadModuleByID looks the requested module ID up in the global registry populated by caddy.RegisterModule (usually via plugin init() functions and modules/standard/imports.go). If the ID is absent it returns 'unknown module: %s'. Common root causes: the plugin is not compiled into this binary, the ID is misspelled, or the namespace/name doesn't match what was registered.

Source

Thrown at context.go:369

	return all, nil
}

// LoadModuleByID decodes rawMsg into a new instance of mod and
// returns the value. If mod.New is nil, an error is returned.
// If the module implements Validator or Provisioner interfaces,
// those methods are invoked to ensure the module is fully
// configured and valid before being used.
//
// This is a lower-level method and will usually not be called
// directly by most modules. However, this method is useful when
// dynamically loading/unloading modules in their own context,
// like from embedded scripts, etc.
func (ctx Context) LoadModuleByID(id string, rawMsg json.RawMessage) (any, error) {
	modulesMu.RLock()
	modInfo, ok := modules[id]
	modulesMu.RUnlock()
	if !ok {
		return nil, fmt.Errorf("unknown module: %s", id)
	}

	if modInfo.New == nil {
		return nil, fmt.Errorf("module '%s' has no constructor", modInfo.ID)
	}

	val := modInfo.New()

	// value must be a pointer for unmarshaling into concrete type, even if
	// the module's concrete type is a slice or map; New() *should* return
	// a pointer, otherwise unmarshaling errors or panics will occur
	if rv := reflect.ValueOf(val); rv.Kind() != reflect.Pointer {
		log.Printf("[WARNING] ModuleInfo.New() for module '%s' did not return a pointer,"+
			" so we are using reflection to make a pointer instead; please fix this by"+
			" using new(Type) or &Type notation in your module's New() function.", id)
		val = reflect.New(rv.Type()).Elem().Addr().Interface().(Module)
	}

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Run `caddy list-modules` and confirm the exact module ID exists in this build; compare character-for-character with the error
  2. If it is a plugin module, build a custom binary: `xcaddy build --with github.com/user/plugin`
  3. Correct the namespace in the config or in the code building the ID (e.g. the caddy:"module=..." tag or the moduleScope passed to loadModuleInline)
  4. Pin matching versions: ensure the plugin version supports your Caddy version's module ID scheme

Example fix

// before
xcaddy build # forgot --with, then config references http.handlers.my_plugin
// after
xcaddy build --with github.com/user/caddy-myplugin
Defensive patterns

Strategy: validation

Validate before calling

// Guard LoadModuleByID calls with a registry check
if !moduleRegistered("http.handlers.foo") {
    return fmt.Errorf("cannot proceed: build custom caddy with the foo plugin")
}
val, err := ctx.LoadModuleByID("http.handlers.foo", raw)

Type guard

func moduleRegistered(id string) bool {
    for _, m := range caddy.Modules() {
        if m == id { return true }
    }
    return false
}

Try / catch

val, err := ctx.LoadModuleByID(id, raw)
if err != nil {
    if strings.Contains(err.Error(), "unknown module") {
        // registry problem: fix build or ID, do not retry
    }
    return err
}

Prevention

When it happens

Trigger: Calling ctx.LoadModuleByID("http.handlers.foo", raw) where no module with that exact ID was registered — plugin not imported, custom build missing it, or the ID string constructed with the wrong namespace/scope (e.g. concatenating an empty moduleScope producing '.foo').

Common situations: Deploying a JSON config produced elsewhere to a stock Caddy binary that lacks third-party plugins; upgrading Caddy where a plugin's module ID changed; typos in module IDs in hand-written JSON; empty namespace producing malformed IDs when loading inline modules.

Related errors


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