lionsoul2014/ip2region · warning

IPSub(%s, %s): %w

Error message

IPSub(%s, %s): %w

What it means

IPMiddle computes the midpoint of two IPs and calls IPAdd internally; if IPAdd fails (e.g. length mismatch), the error is wrapped as IPSub(%s, %s): %w with both IPs stringified. Note the message says IPSub but the failing call is IPAdd — the wrap is mislabeled.

Source

Thrown at binding/golang/xdb/util.go:132

	var carry byte = 0

	for i := 0; i < length; i++ {
		// 1. Shift current byte right by 1
		// 2. Or (|) with the carry from the previous byte (shifted to the MSB position)
		result[i] = (ip[i] >> 1) | (carry << 7)

		// 3. Capture the Least Significant Bit (LSB) to use as carry for the next byte
		carry = ip[i] & 1
	}

	return result
}

// IPMiddle get the middle value of two input ip address
func IPMiddle(sip, eip []byte) ([]byte, error) {
	buf, err := IPAdd(sip, eip)
	if err != nil {
		return []byte{}, fmt.Errorf("IPSub(%s, %s): %w", IP2String(sip), IP2String(eip), err)
	}

	return IPHalf(buf), nil
}

// Verify if the current Searcher could be used to search the specified xdb file.
// Why do we need this check ?
// The future features of the xdb impl may cause the current searcher not able to work properly.
//
// @Note: You Just need to check this ONCE when the service starts
// Or use another process (eg, A command) to check once Just to confirm the suitability.
func Verify(handle *os.File) error {
	header, err := LoadHeader(handle)
	if err != nil {
		return fmt.Errorf("loading header: %w", err)
	}

	// get the runtime ptr bytes

View on GitHub (pinned to c1a1fc7d59)

Solutions

  1. Make both inputs the same byte length (both To4 or both To16) before calling IPMiddle
  2. Read the wrapped %w error — it will be "length of the two ips are not the same" from IPAdd
  3. Ensure the sip <= eip pair belongs to the same IP version in your range data
  4. Ignore the misleading IPSub prefix; it is a known labeling quirk in this code path

Example fix

// before
m, err := xdb.IPMiddle(sip4, eip6) // IPSub(...): length of the two ips are not the same
// after
if len(sip4) != len(eip6) { return errors.New("range spans v4/v6") }
mid, err := xdb.IPMiddle(net.IP(sip4).To16(), net.IP(eip6).To16())
Defensive patterns

Strategy: type-guard

Validate before calling

func canIPMiddle(sip, eip []byte) bool {
    return len(sip) == len(eip) && len(sip) > 0
}

Type guard

func isSameFamily(sip, eip []byte) bool {
    return len(sip) > 0 && len(sip) == len(eip)
}

Try / catch

mid, err := xdb.IPMiddle(sip, eip)
if err != nil {
    // note: wrapped error mentions IPSub but originates from IPAdd
    return fmt.Errorf("ip middle (%d/%d bytes): %w", len(sip), len(eip), err)
}

Prevention

When it happens

Trigger: Calling IPMiddle with a 4-byte and a 16-byte IP; passing empty slices (length 0 vs 0 passes, but 0 vs non-0 fails); calling with IPs from different address families.

Common situations: Splitting IP ranges from a data file that contains both v4 and v6 segments; refactoring code that changed one operand to To16 but not the other; unit tests feeding heterogeneous slices.

Related errors


AI-assisted analysis of lionsoul2014/ip2region@c1a1fc7d59 (2026-09-02). Data as JSON: /api/errors/cc78fed100201c52. Report an issue: GitHub.