shadow1ng/fscan · error
fscan: target cannot set both Host and URL
Error message
fscan: target cannot set both Host and URL
What it means
validateConfig in pkg/fscan/scanner.go enforces that each scan Target identifies itself either by Host or by URL, never both. Setting both is ambiguous (the scanner cannot decide which form to normalize into fscan's flag variables), so the config is rejected before any scan starts. This is a fail-fast precondition check run by ValidateConfig and scanEach.
Source
Thrown at pkg/fscan/scanner.go:350
func validateConfig(config Config, targets []Target) error {
if len(targets) == 0 {
return fmt.Errorf("fscan: at least one target is required")
}
for _, name := range normalizePlugins(config.Plugins) {
if !plugins.Exists(name) {
return fmt.Errorf("fscan: plugin %q not found", name)
}
if !config.AllowUnsafePlugins && !IsSafePlugin(name) {
return fmt.Errorf("fscan: plugin %q is not enabled for embedded safe mode", name)
}
}
for _, target := range targets {
if strings.TrimSpace(target.Host) == "" && strings.TrimSpace(target.URL) == "" {
return fmt.Errorf("fscan: target host or URL is required")
}
if strings.TrimSpace(target.Host) != "" && strings.TrimSpace(target.URL) != "" {
return fmt.Errorf("fscan: target cannot set both Host and URL")
}
for _, port := range target.Ports {
if port < 1 || port > 65535 {
return fmt.Errorf("fscan: invalid port %d", port)
}
}
}
for _, port := range config.Ports {
if port < 1 || port > 65535 {
return fmt.Errorf("fscan: invalid port %d", port)
}
}
return nil
}
func buildFlagVars(config Config, target Target) *common.FlagVars {
timeout := secondsOrDefault(config.Timeout, common.DefaultTimeout)
webTimeout := secondsOrDefault(config.WebTimeout, 5)View on GitHub (pinned to 95cc12e753)
Solutions
- Set only Host (with Ports) for raw host/port scanning; clear URL.
- Set only URL when scanning an HTTP endpoint; clear Host and let the scanner parse it.
- Add a pre-submit check in your tooling that rejects Targets with both fields set.
- If you truly need both host and URL info, derive the URL from Host at call time instead of passing both.
Example fix
// before
targets := []fscan.Target{{Host: "10.0.0.5", URL: "http://10.0.0.5:8080"}}
// after
targets := []fscan.Target{{URL: "http://10.0.0.5:8080"}}
// or for port scanning:
targets := []fscan.Target{{Host: "10.0.0.5", Ports: []int{80, 8080}}} Defensive patterns
Strategy: validation
Validate before calling
for i, t := range targets {
if strings.TrimSpace(t.Host) != "" && strings.TrimSpace(t.URL) != "" {
return fmt.Errorf("target[%d]: set either Host or URL, not both", i)
}
} Type guard
func hasAmbiguousTarget(t fscan.Target) bool {
return strings.TrimSpace(t.Host) != "" && strings.TrimSpace(t.URL) != ""
} Try / catch
if err := fscan.ValidateConfig(cfg); err != nil {
if strings.Contains(err.Error(), "cannot set both Host and URL") {
// fix target definitions before retry
}
return err
} Prevention
- Use constructor helpers that take either a host or a URL, never raw struct literals.
- Add a CI/config-lint rule rejecting Target objects with both fields populated.
- Clear the sibling field whenever you populate one of Host/URL.
When it happens
Trigger: Calling ValidateConfig(config) or Scan/scanEach where targets contains a Target whose Host is a non-empty string AND whose URL is a non-empty string simultaneously.
Common situations: Populating Target structs from YAML/JSON where both fields were copied from a template; programmatically building targets from a host inventory that also recorded a scheme'd URL; reusing a struct filled by a previous code path that set URL and then adding Host for a port sweep.
Related errors
- initialize http client: %w
- parse target failed: %w
- fscan: invalid port %d
- webscan_fscan_format_parse_failed
AI-assisted analysis of shadow1ng/fscan@95cc12e753 (2026-09-06).
Data as JSON: /api/errors/8fb9b2c7ec82f981.
Report an issue: GitHub.