caddyserver/caddy · error
cannot add operation: maximum %d operations allowed
Error message
cannot add operation: maximum %d operations allowed
What it means
MultiRegexpFilter.AddOperation returns this when the filter already holds maxRegexpOperations (50) entries and another is added. AddOperation is used when merging multiple RegexpFilter instances into one MultiRegexpFilter, so this protects the same 50-op ceiling during merge as Validate does at config time.
Source
Thrown at modules/logging/filters.go:1000
for _, op := range f.Operations {
// Each regexp operation is applied sequentially
// Using RE2 engine which is safe from ReDoS attacks
result = op.regexp.ReplaceAllString(result, op.Value)
// Ensure result doesn't exceed max length after each operation
if len(result) > maxInputLength {
result = result[:maxInputLength]
}
}
return result
}
// AddOperation adds a single regexp operation to the filter with validation.
// This is used when merging multiple RegexpFilter instances.
func (f *MultiRegexpFilter) AddOperation(rawRegexp, value string) error {
// Security checks
if len(f.Operations) >= maxRegexpOperations {
return fmt.Errorf("cannot add operation: maximum %d operations allowed", maxRegexpOperations)
}
if rawRegexp == "" {
return fmt.Errorf("regexp pattern cannot be empty")
}
if len(rawRegexp) > maxPatternLength {
return fmt.Errorf("regexp pattern too long: %d characters (maximum %d)", len(rawRegexp), maxPatternLength)
}
f.Operations = append(f.Operations, regexpFilterOperation{
RawRegexp: rawRegexp,
Value: value,
})
return nil
}
// RenameFilter is a Caddy log field filter thatView on GitHub (pinned to 50e54ee279)
Solutions
- Reduce the number of regexp filters being merged (drop or combine entries)
- Check len(f.Operations) < 50 before each AddOperation call in your own code and stop or split gracefully
- Restructure to explicit multi_regexp blocks you control, staying under 50
Example fix
// before
for _, op := range ops {
_ = mf.AddOperation(op.search, op.replace) // fails silently past 50
}
// after
for _, op := range ops {
if err := mf.AddOperation(op.search, op.replace); err != nil {
log.Fatal(err) // or split into a new MultiRegexpFilter
}
} Defensive patterns
Strategy: validation
Validate before calling
if len(mf.Operations) >= 50 {
return fmt.Errorf("cannot add operation %d: split the filter", len(mf.Operations))
}
if err := mf.AddOperation(search, replace); err != nil { return err } Try / catch
if err := mf.AddOperation(p, v); err != nil {
if strings.Contains(err.Error(), "maximum") { /* split into a new filter */ }
return err
} Prevention
- Track the running operation count in merge loops
- Design merge code to spill into a new MultiRegexpFilter at the cap
When it happens
Trigger: Calling AddOperation on a MultiRegexpFilter that already has 50 operations, e.g. when Caddy (or plugin code) merges several single RegexpFilter configs into a multi filter and the combined count exceeds 50.
Common situations: A config lists many individual 'filter': 'regexp' entries that the logging pipeline coalesces into a MultiRegexpFilter; plugin authors building filters programmatically in a loop without checking the cap.
Related errors
- regexp pattern too long: %d characters (maximum %d)
- regexp pattern cannot be empty
- server %s: logger name must not have a port: %s
- provisioning fallback encoder module: %v
- loading wrapped encoder module: %v
AI-assisted analysis of caddyserver/caddy@50e54ee279 (2026-08-15).
Data as JSON: /api/errors/8b25b1716c8727e0.
Report an issue: GitHub.