projectdiscovery/nuclei · error
headless mode (-headless) is required if -ho, -sb, -sc or -l
Error message
headless mode (-headless) is required if -ho, -sb, -sc or -lha are set
What it means
Nuclei configures its network dialer during protocol state initialization. When both -source-ip and -interface are set, isIpAssociatedWithInterface must confirm the IP is assigned to that interface; a mismatch aborts startup. This prevents binding traffic to an address the OS would not route through that interface.
Source
Thrown at internal/runner/options.go:160
// validateOptions validates the configuration options passed
func ValidateOptions(options *types.Options) error {
if err := validateOptions.Struct(options); err != nil {
if _, ok := err.(*validator.InvalidValidationError); ok {
return err
}
errs := []string{}
for _, err := range err.(validator.ValidationErrors) {
errs = append(errs, err.Namespace()+": "+err.Tag())
}
return errors.Wrap(errors.New(strings.Join(errs, ", ")), "validation failed for these fields")
}
if options.Verbose && options.Silent {
return errors.New("both verbose and silent mode specified")
}
if (options.HeadlessOptionalArguments != nil || options.ShowBrowser || options.UseInstalledChrome) && !options.Headless {
return errors.New("headless mode (-headless) is required if -ho, -sb, -sc or -lha are set")
}
if options.FollowHostRedirects && options.FollowRedirects {
return errors.New("both follow host redirects and follow redirects specified")
}
if options.ShouldFollowHTTPRedirects() && options.DisableRedirects {
return errors.New("both follow redirects and disable redirects specified")
}
// loading the proxy server list from file or cli and test the connectivity
if err := loadProxyServers(options); err != nil {
return err
}
if options.Validate {
validateTemplatePaths(options.Logger, config.DefaultConfig.TemplatesDirectory, options.Templates, options.Workflows)
}
if options.DAST {
if err := validateDASTOptions(options); err != nil {
return errView on GitHub (pinned to 265b3a3dec)
Solutions
- Run ip addr show <interface> and use one of its addresses in -source-ip
- Point -interface at the interface that actually owns the IP
- Drop one of the two flags and let the other drive selection alone
Example fix
# before nuclei -source-ip 10.0.0.5 -interface eth0 # eth0 actually has 192.168.1.20 # after nuclei -source-ip 192.168.1.20 -interface eth0
Defensive patterns
Strategy: validation
Validate before calling
import "net"
func ipOnInterface(ip, iface string) bool {
i, err := net.InterfaceByName(iface)
if err != nil {
return false
}
addrs, _ := i.Addrs()
for _, a := range addrs {
if ipnet, ok := a.(*net.IPNet); ok && ipnet.IP.String() == ip {
return true
}
}
return false
} Prevention
- Derive -source-ip from ip addr output instead of hard-coding
- In scripts, resolve the interface's address dynamically
- Drop -interface when unsure; -source-ip alone is enough
When it happens
Trigger: nuclei -source-ip 192.168.1.10 -interface eth0 where eth0 holds a different address or only IPv6; equivalent lib/nuclei options with both fields set.
Common situations: VPN environments where the IP lives on utun/wg0 while the flag names eth0; container or NAT setups; command lines copied between machines.
Related errors
- both follow host redirects and follow redirects specified
- both follow redirects and disable redirects specified
- prompt not found (read cap reached)
- include directive preprocessing is disabled
- could not read profile file: %w
AI-assisted analysis of projectdiscovery/nuclei@265b3a3dec (2026-08-15).
Data as JSON: /api/errors/b9b4c35e5171766d.
Report an issue: GitHub.