shadow1ng/fscan · error

parser_invalid_start_ip

Error message

parser_invalid_start_ip

What it means

The start portion of an IP range string failed net.ParseIP. newRangeHostSource trims the text before the dash and requires it to be a valid IP literal; anything else (hostname, octet out of range like 300.1.1.1, empty string) yields this message naming the bad start value.

Solutions

  1. Correct the start IP so net.ParseIP accepts it, e.g. "192.168.1.10-192.168.1.20".
  2. Validate with net.ParseIP(strings.TrimSpace(start)) != nil in config-loading code before calling addRange.
  3. If a hostname is intended, resolve it with net.LookupHost first and use the returned IP.

Example fix

// before
m.AddRange("192.168.1.256-192.168.1.260") // parser_invalid_start_ip

// after
start := net.ParseIP("192.168.1.250")
if start == nil { return fmt.Errorf("bad start IP") }
m.AddRange("192.168.1.250-192.168.1.260")
Defensive patterns

Strategy: validation

Validate before calling

func validStartIP(rng string) bool {
    parts := strings.SplitN(rng, "-", 2)
    if len(parts) != 2 {
        return false
    }
    return net.ParseIP(strings.TrimSpace(parts[0])) != nil
}

Try / catch

if err := matcher.AddRange(rng); err != nil {
    return fmt.Errorf("range %q rejected: %w", rng, err)
}

Prevention

When it happens

Trigger: addRange/newHostSource with "example.com-10.0.0.5", "300.0.0.1-10.0.0.2", "-10.0.0.5", or any non-IP text left of the dash.

Common situations: Users entering hostnames instead of IPs; mistyped octets (e.g. 192.168.1.256); whitespace or invisible characters from YAML/CSV fields that TrimSpace does not remove (e.g. NBSP).

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of shadow1ng/fscan@95cc12e753 (2026-09-06). Data as JSON: /api/errors/ebeb661f4c4ede36. Report an issue: GitHub.

Appendix: source

Thrown at common/parsers/host_iterator.go:354

	end := start + uint32(size-1)
	if size > 2 {
		start++
		end--
	}
	return &cidrHostSource{current: start, end: end}, nil
}

func newRangeHostSource(rangeStr string) (hostSource, error) {
	parts := strings.Split(rangeStr, "-")
	if len(parts) != 2 {
		return nil, fmt.Errorf("%s", i18n.Tr("parser_invalid_ip_range_fmt", rangeStr))
	}

	startIPStr := strings.TrimSpace(parts[0])
	endIPStr := strings.TrimSpace(parts[1])
	startIP := net.ParseIP(startIPStr)
	if startIP == nil {
		return nil, fmt.Errorf("%s", i18n.Tr("parser_invalid_start_ip", startIPStr))
	}

	if len(endIPStr) < 4 || !strings.Contains(endIPStr, ".") {
		endNum, err := strconv.Atoi(endIPStr)
		if err != nil || endNum > 255 {
			return nil, fmt.Errorf("%s", i18n.Tr("parser_invalid_ip_end_val", endIPStr))
		}
		parts := strings.Split(startIPStr, ".")
		if len(parts) != 4 {
			return nil, fmt.Errorf("%s", i18n.Tr("parser_invalid_ip_fmt", startIPStr))
		}
		parts[3] = strconv.Itoa(endNum)
		endIPStr = strings.Join(parts, ".")
	}

	start, ok := ipToUint32(startIP)
	if !ok {
		return nil, fmt.Errorf("%s", i18n.GetText("parser_ipv4_only"))

View on GitHub (pinned to 95cc12e753)