caddyserver/caddy · error

when both include and exclude are populated, each element mu

Error message

when both include and exclude are populated, each element must be a superspace or subspace of one in the other list; check '%s' in include

What it means

When both include and exclude are populated, every include entry must be a superspace or subspace of some exclude entry (dot-delimited namespace prefix relationship). An include entry that is unrelated to all exclude entries is neither a rule nor an exception, so validation fails naming the offending include element.

Source

Thrown at logging.go:519

	// sets do not intersect, which is also a contradiction.
	if len(cl.Include) > 0 && len(cl.Exclude) > 0 {
		// prevent intersections
		for _, allow := range cl.Include {
			if slices.Contains(cl.Exclude, allow) {
				return fmt.Errorf("include and exclude must not intersect, but found %s in both lists", allow)
			}
		}

		// ensure namespaces are nested
	outer:
		for _, allow := range cl.Include {
			for _, deny := range cl.Exclude {
				if strings.HasPrefix(allow+".", deny+".") ||
					strings.HasPrefix(deny+".", allow+".") {
					continue outer
				}
			}
			return fmt.Errorf("when both include and exclude are populated, each element must be a superspace or subspace of one in the other list; check '%s' in include", allow)
		}
	}
	return nil
}

func (cl *CustomLog) matchesModule(moduleID string) bool {
	return cl.loggerAllowed(moduleID, true)
}

// loggerAllowed returns true if name is allowed to emit
// to cl. isModule should be true if name is the name of
// a module and you want to see if ANY of that module's
// logs would be permitted.
func (cl *CustomLog) loggerAllowed(name string, isModule bool) bool {
	// accept all loggers by default
	if len(cl.Include) == 0 && len(cl.Exclude) == 0 {
		return true
	}

View on GitHub (pinned to 50e54ee279)

Solutions

  1. Restructure so each exclude entry is a parent or child namespace of an include entry (e.g. include "http", exclude "http.handlers.file_server").
  2. Split unrelated include/exclude pairs into separate log blocks, each self-consistent.
  3. Use include only (plus 'exclude' removed) when you simply want an allowlist.

Example fix

// before (caddyfile)
log {
    include http.handlers.reverse_proxy
    exclude tls.issuance
}

// after — split into consistent sinks
log rp {
    include http.handlers.reverse_proxy
    output file /var/log/caddy/rp.log
}
log tls {
    include tls
    exclude tls.issuance.acme
    output file /var/log/caddy/tls.log
}
Defensive patterns

Strategy: validation

Validate before calling

// enforce the nesting rule client-side before load
func nested(include, exclude []string) error {
outer:
    for _, inc := range include {
        for _, exc := range exclude {
            if strings.HasPrefix(inc+".", exc+".") || strings.HasPrefix(exc+".", inc+".") {
                continue outer
            }
        }
        return fmt.Errorf("include %s is unrelated to every exclude entry", inc)
    }
    return nil
}

Try / catch

if err := caddy.Validate(cfg); err != nil {
    if strings.Contains(err.Error(), "superspace or subspace") {
        // restructure into rule+exception pairs or split sinks
    }
    return err
}

Prevention

When it happens

Trigger: include ["http.handlers.reverse_proxy"] with exclude ["tls"] — neither string is a dot-prefix of the other, so the pair is rejected. Nested pairs like include ["http"] / exclude ["http.handlers"] are fine because one contains the other.

Common situations: Trying to collect logs from unrelated subsystems while excluding an unrelated third subsystem in the same log sink; assuming include/exclude act as independent allow/deny lists (they don't when both are set — they must form rule+exception pairs).

Related errors


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