shadow1ng/fscan · error · ErrInvalidURL
%w: empty host (ErrInvalidURL)
Error message
%w: empty host (ErrInvalidURL)
What it means
buildTargetURL parses a target URL and returns a wrapped ErrInvalidURL when the parsed URL has no host component. This happens when info.URL parses successfully (url.Parse rarely errors) but contains no hostname, e.g. a relative path or scheme-only string. The sentinel ErrInvalidURL lets callers check errors.Is(err, ErrInvalidURL).
Source
Thrown at webscan/web_scan.go:115
func buildTargetURL(info *common.HostInfo) (string, error) {
// 自动构建URL
if info.URL == "" {
protocol := protocolHTTP
if isTLSPort(info.Port) {
protocol = protocolHTTPS
}
info.URL = protocol + net.JoinHostPort(info.Host, fmt.Sprint(info.Port))
} else if !hasProtocolPrefix(info.URL) {
info.URL = protocolHTTP + normalizeSchemelessWebTarget(info.URL)
}
// 解析URL以提取基础部分
parsedURL, err := url.Parse(info.URL)
if err != nil {
return "", fmt.Errorf("%w: %w", ErrInvalidURL, err)
}
if parsedURL.Hostname() == "" {
return "", fmt.Errorf("%w: empty host", ErrInvalidURL)
}
portStr := parsedURL.Port()
if portStr == "" {
if hasMalformedWebURLPort(parsedURL.Host) {
return "", fmt.Errorf("%w: invalid port", ErrInvalidURL)
}
} else {
port, err := strconv.Atoi(portStr)
if err != nil || port < 1 || port > 65535 {
return "", fmt.Errorf("%w: invalid port %q", ErrInvalidURL, portStr)
}
}
parsedURL.Host = normalizeWebURLHost(parsedURL.Host)
return fmt.Sprintf("%s://%s", parsedURL.Scheme, parsedURL.Host), nil
}
// hasProtocolPrefix 检查URL是否包含协议前缀View on GitHub (pinned to 95cc12e753)
Solutions
- Ensure info.URL includes scheme and host, e.g. http://example.com
- Validate with url.Parse and check parsedURL.Hostname() != "" before calling WebScan
- Normalize bare hosts by prefixing a default scheme (http://) in the input pipeline
- Handle errors.Is(err, ErrInvalidURL) by rejecting/flagging the target
Example fix
// before
WebScan(target)
// after
u, err := url.Parse(target)
if err != nil || u.Hostname() == "" {
target = "http://" + strings.TrimPrefix(target, "//")
}
WebScan(target) Defensive patterns
Strategy: validation
Validate before calling
u, err := url.Parse(target)
if err != nil || u.Hostname() == "" {
return fmt.Errorf("target %q has no host", target)
} Type guard
func hasHost(raw string) bool {
u, err := url.Parse(raw)
return err == nil && u.Hostname() != ""
} Try / catch
if err := WebScan(target); err != nil {
if errors.Is(err, ErrInvalidURL) {
log.Printf("skipping invalid target %q: %v", target, err)
return nil
}
return err
} Prevention
- Always store targets with scheme+host (http://host)
- Normalize bare hosts with a default scheme at ingestion
- Unit-test buildTargetURL against relative/empty URLs
When it happens
Trigger: WebScan calls buildTargetURL with info.URL that yields parsedURL.Hostname() == "": e.g. "/admin", "http:///path", "example.com/path" (no scheme), or "".
Common situations: Scanner config where the URL column lost its scheme ("example.com" instead of "http://example.com"), CSV/Excel imports trimming the scheme, or hand-built URLs from host+path concatenation missing the scheme.
Understand the failure class
Background: "Invalid URL" / "URL cannot be empty": fix the malformed or missing URL behind request-construction failures — this error's family across 50 libraries.
Related errors
- %w: invalid port (ErrInvalidURL)
- result cannot be nil
- result cannot be nil
- output file not specified
- invalid output format: %s
AI-assisted analysis of shadow1ng/fscan@95cc12e753 (2026-09-06).
Data as JSON: /api/errors/6cb7aaf5094fcdd7.
Report an issue: GitHub.