shadow1ng/fscan · error

parser_invalid_ip_fmt

Error message

parser_invalid_ip_fmt

What it means

When the short-tail branch rewrites the start IP's last octet, it requires the start string to contain exactly four dot-separated octets. If strings.Split(startIPStr, ".") yields anything other than 4 parts, this formatted message naming the start string is returned. Note startIP already parsed as an IP, so this mainly guards odd canonical forms before octet substitution.

Solutions

  1. Pass the start IP in standard dotted-quad form, e.g. "192.168.1.1-50".
  2. Normalize the string first: verify strings.Count(start, ".") == 3 before addRange.
  3. Avoid abbreviated IPv4 forms (e.g. "127.1") which net.ParseIP may accept but lack four dotted parts.

Example fix

// before
m.AddRange("192.168.1-50") // ambiguous; may hit parser_invalid_ip_fmt

// after
m.AddRange("192.168.0.1-50") // full dotted-quad start plus numeric tail
Defensive patterns

Strategy: validation

Validate before calling

func startIsDottedQuad(rng string) bool {
    parts := strings.SplitN(rng, "-", 2)
    if len(parts) != 2 {
        return false
    }
    return len(strings.Split(strings.TrimSpace(parts[0]), ".")) == 4
}

Try / catch

if err := matcher.AddRange(rng); err != nil {
    return fmt.Errorf("normalize %q to dotted-quad: %w", rng, err)
}

Prevention

When it happens

Trigger: addRange with a start IP that parses but does not have 4 dot-separated labels in its original string form combined with a numeric short tail, e.g. unusual formatting of the start portion in "ip-tail" notation.

Common situations: Programmatically generated range strings using abbreviated IP forms; localization/formatting layers that mangle dotted-quad strings before they reach addRange.

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/a30b57b94ebc3735. Report an issue: GitHub.

Appendix: source

Thrown at common/parsers/host_iterator.go:364

	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"))
	}
	end, ok := ipToUint32(net.ParseIP(endIPStr))
	if !ok {
		return nil, fmt.Errorf("%s", i18n.Tr("parser_invalid_end_ip", endIPStr))
	}
	if start > end {
		return nil, fmt.Errorf("%s", i18n.GetText("parser_start_gt_end"))
	}
	return &cidrHostSource{current: start, end: end}, nil
}

View on GitHub (pinned to 95cc12e753)