symfony/http-foundation · error · InvalidArgumentException

Cannot anonymize more than 4 bytes for IPv4 and 16 bytes…

Error message

Cannot anonymize more than 4 bytes for IPv4 and 16 bytes for IPv6.

What it means

IpUtils::anonymize() caps truncation at 4 bytes for IPv4 and 16 bytes for IPv6, because that is the entire address; anything larger is meaningless and would attempt to shift more bits than exist. Passing an exceeding byte count throws this InvalidArgumentException before any anonymization happens.

Solutions

  1. Clamp per family: $v4Bytes = min(4, $v4Bytes); $v6Bytes = min(16, $v6Bytes);
  2. If the goal is full anonymization, pass the exact maxima: anonymize($ip, 4, 16).
  3. Separate the IPv4 and IPv6 byte settings in your configuration instead of sharing one value.
  4. Validate config-supplied integers against int<0,4> / int<0,16> at load time.
  5. Use filter_var to detect address family first and apply only the matching limit.

Example fix

// before
IpUtils::anonymize($ip, $bytes, $bytes); // $bytes = 16: v4 limit is 4, throws

// after
IpUtils::anonymize($ip, min(4, $bytes), min(16, $bytes));
Defensive patterns

Strategy: validation

Validate before calling

// before calling anonymize
$v4Bytes = min(4, max(0, (int) $v4Bytes));
$v6Bytes = min(16, max(0, (int) $v6Bytes));
IpUtils::anonymize($ip, $v4Bytes, $v6Bytes);

Type guard

function isValidAnonymizeBytes(int $v4, int $v6): bool {
    return $v4 >= 0 && $v4 <= 4 && $v6 >= 0 && $v6 <= 16;
}

Try / catch

try {
    $anon = IpUtils::anonymize($ip, $v4Bytes, $v6Bytes);
} catch (\InvalidArgumentException $e) {
    $anon = IpUtils::anonymize($ip, 4, 16); // full anonymization fallback
}

Prevention

When it happens

Trigger: Calling IpUtils::anonymize($ip, 5) or IpUtils::anonymize($ip, 4, 17); using a single shared 'bytes' config value applied to both IPv4 and IPv6 without clamping to each limit (e.g. 8 or 16 bytes, valid for v6 but over the v4 cap of 4).

Common situations: One privacy config (e.g. 'anonymize: 16') fed to both v4/v6 arguments; developers assuming the limit is uniform across families; off-by-one when the intent was 'anonymize fully' (use 4/16 exactly).

Related errors


AI-assisted analysis of symfony/http-foundation@5aea19cd67 (2026-09-13). Data as JSON: /api/errors/854065df6766bb4d. Report an issue: GitHub.

Appendix: source

Thrown at IpUtils.php:214

        return self::setCacheResult($cacheKey, true);
    }

    /**
     * Anonymizes an IP/IPv6.
     *
     * Removes the last bytes of IPv4 and IPv6 addresses (1 byte for IPv4 and 8 bytes for IPv6 by default).
     *
     * @param int<0, 4>  $v4Bytes
     * @param int<0, 16> $v6Bytes
     */
    public static function anonymize(string $ip, int $v4Bytes = 1, int $v6Bytes = 8): string
    {
        if ($v4Bytes < 0 || $v6Bytes < 0) {
            throw new \InvalidArgumentException('Cannot anonymize less than 0 bytes.');
        }

        if ($v4Bytes > 4 || $v6Bytes > 16) {
            throw new \InvalidArgumentException('Cannot anonymize more than 4 bytes for IPv4 and 16 bytes for IPv6.');
        }

        /*
         * If the IP contains a % symbol, then it is a local-link address with scoping according to RFC 4007
         * In that case, we only care about the part before the % symbol, as the following functions, can only work with
         * the IP address itself. As the scope can leak information (containing interface name), we do not want to
         * include it in our anonymized IP data.
         */
        if (str_contains($ip, '%')) {
            $ip = substr($ip, 0, strpos($ip, '%'));
        }

        $wrappedIPv6 = false;
        if (str_starts_with($ip, '[') && str_ends_with($ip, ']')) {
            $wrappedIPv6 = true;
            $ip = substr($ip, 1, -1);
        }

View on GitHub (pinned to 5aea19cd67)