kgretzky/evilginx2 · error
sub_filters: missing `replace` field
Error message
sub_filters: missing `replace` field
What it means
Each `sub_filters` entry requires a `replace` string substituted for the `search` regex matches. The entry's `replace` field was nil, so validation fails. This is the last mandatory sub_filter field checked; `with_params` is optional and defaults to an empty list when absent.
Source
Thrown at core/phishlet.go:454
if fp.SubFilters != nil {
for _, sf := range *fp.SubFilters {
if sf.Hostname == nil {
return fmt.Errorf("sub_filters: missing `triggers_on` field")
}
if sf.Sub == nil {
return fmt.Errorf("sub_filters: missing `orig_sub` field")
}
if sf.Domain == nil {
return fmt.Errorf("sub_filters: missing `domain` field")
}
if sf.Mimes == nil {
return fmt.Errorf("sub_filters: missing `mimes` field")
}
if sf.Search == nil {
return fmt.Errorf("sub_filters: missing `search` field")
}
if sf.Replace == nil {
return fmt.Errorf("sub_filters: missing `replace` field")
}
if sf.WithParams == nil {
sf.WithParams = &[]string{}
}
for n := range *sf.Mimes {
(*sf.Mimes)[n] = p.paramVal((*sf.Mimes)[n])
}
p.addSubFilter(p.paramVal(*sf.Hostname), p.paramVal(*sf.Sub), p.paramVal(*sf.Domain), *sf.Mimes, p.paramVal(*sf.Search), p.paramVal(*sf.Replace), sf.RedirectOnly, *sf.WithParams)
}
}
if fp.JsInject != nil {
for _, js := range *fp.JsInject {
if js.TriggerDomains == nil {
return fmt.Errorf("js_inject: missing `trigger_domains` field")
}
if js.TriggerPaths == nil {
return fmt.Errorf("js_inject: missing `trigger_paths` field")View on GitHub (pinned to 4c0988a1d9)
Solutions
- Add `replace: '<replacement>'` (typically using the `{domain}` placeholder, e.g. `replace: 'accounts.{domain}'`) to the entry
- Confirm search and replace are consistent with each other (the replacement should re-substitute the phishing domain)
- Reload the phishlet; all remaining sub_filter checks should now pass
Example fix
// before
sub_filters:
- triggers_on: 'auth.example.com'
orig_sub: 'accounts'
domain: 'example.com'
mimes: ['text/html']
search: 'accounts\\.example\\.com'
// after
sub_filters:
- triggers_on: 'auth.example.com'
orig_sub: 'accounts'
domain: 'example.com'
mimes: ['text/html']
search: 'accounts\\.example\\.com'
replace: 'accounts.{domain}' Defensive patterns
Strategy: validation
Validate before calling
for i, sf in enumerate(cfg.get('sub_filters') or []):
if sf.get('replace') is None:
raise ValueError(f"sub_filters[{i}]: missing `replace` field") Type guard
func validSubFilter(sf SubFilter) bool { return sf.Replace != nil } Prevention
- Use the `{domain}` placeholder in replace for the phishing host
- Always pair search+replace in the same edit
- Note `with_params` is optional — omitting it is safe, unlike replace
When it happens
Trigger: A `sub_filters:` entry omits `replace:`; core/phishlet.go's `if sf.Replace == nil` check fires after all other required fields validate.
Common situations: Truncating the entry after `search` while editing; assuming search-only filters are allowed; paste from docs cut off before the replace line.
Understand the failure class
Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.
Related errors
- sub_filters: missing `domain` field
- sub_filters: missing `mimes` field
- sub_filters: missing `search` field
- sub_filters: missing `triggers_on` field
- sub_filters: missing `orig_sub` field
AI-assisted analysis of kgretzky/evilginx2@4c0988a1d9 (2026-09-05).
Data as JSON: /api/errors/d50e42fb36be73ee.
Report an issue: GitHub.