caddyserver/caddy · error

module value cannot be null

Error message

module value cannot be null

What it means

After decoding, LoadModuleByID rejects an explicitly null module value: 'module value cannot be null'. Because New() always returns a non-nil pointer, this triggers when the JSON config declares the module as null and the loader treats the resulting state as an attempted null module — which is almost always a config smell rather than a legitimate setup.

Source

Thrown at context.go:402

			" using new(Type) or &Type notation in your module's New() function.", id)
		val = reflect.New(rv.Type()).Elem().Addr().Interface().(Module)
	}

	// fill in its config only if there is a config to fill in
	if len(rawMsg) > 0 {
		err := StrictUnmarshalJSON(rawMsg, &val)
		if err != nil {
			return nil, fmt.Errorf("decoding module config: %s: %v", modInfo, err)
		}
	}

	if val == nil {
		// returned module values are almost always type-asserted
		// before being used, so a nil value would panic; and there
		// is no good reason to explicitly declare null modules in
		// a config; it might be because the user is trying to achieve
		// a result the developer isn't expecting, which is a smell
		return nil, fmt.Errorf("module value cannot be null")
	}

	var err error

	// if this is an app module, keep a reference to it,
	// since submodules may need to reference it during
	// provisioning (even though the parent app module
	// may not be fully provisioned yet; this is the case
	// with the tls app's automation policies, which may
	// refer to the tls app to check if a global DNS
	// module has been configured for DNS challenges)
	if appModule, ok := val.(App); ok {
		ctx.cfg.apps[id] = appModule
		defer func() {
			if err != nil {
				ctx.cfg.failedApps[id] = err
			}
		}()

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Remove the null entry entirely from the JSON config instead of setting it to null
  2. If templating, emit conditional blocks that omit keys rather than render "key": null
  3. Validate with `caddy validate` to catch it before reload/deploy

Example fix

// before
{ "apps": { "http": null } }
// after
{ "apps": { } } // or omit the key entirely
Defensive patterns

Strategy: validation

Validate before calling

// Strip explicit nulls from templated configs before applying
func stripNulls(v any) any {
    switch t := v.(type) {
    case map[string]any:
        for k, val := range t {
            if val == nil { delete(t, k) } else { t[k] = stripNulls(val) }
        }
    case []any:
        for i := range t { t[i] = stripNulls(t[i]) }
    }
    return v
}

Try / catch

if _, err := ctx.LoadModuleByID(id, raw); err != nil { return err } // 'cannot be null' means remove the key

Prevention

When it happens

Trigger: A JSON config contains "some_module": null where a module object is expected (e.g. an app or inline module set to null), or a map entry whose value is JSON null passed through to LoadModuleByID with empty rawMsg handling.

Common situations: Templated configs (Helm, envsubst) rendering optional blocks as null instead of omitting them; disabling a feature by nulling it out; JSON merge patches leaving explicit nulls.

Related errors


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