getgrav/grav · error · InvalidArgumentException

Uri host must be a string

Error message

Uri host must be a string

What it means

UriPartsFilter::filterHost() asserts the host is a string before normalizing (IPv6 bracketing, hostname regex, lowercasing), throwing InvalidArgumentException otherwise. It backs AbstractUri::withHost() and the Uri parts constructor; since withHost() declares a native `string` parameter, the is_string throw is mostly reachable via direct filterHost() calls or untyped callers passing null/array.

Source

Thrown at system/src/Grav/Framework/Uri/UriPartsFilter.php:66

            throw new InvalidArgumentException('Uri user info must be a string');
        }

        return preg_replace_callback(
            '/(?:[^a-zA-Z0-9_\-\.~!\$&\'\(\)\*\+,;=]+|%(?![A-Fa-f0-9]{2}))/u',
            fn($match) => rawurlencode((string) $match[0]),
            $info
        ) ?? '';
    }

    /**
     * @param string $host
     * @return string
     * @throws InvalidArgumentException If the host is invalid.
     */
    public static function filterHost($host)
    {
        if (!is_string($host)) {
            throw new InvalidArgumentException('Uri host must be a string');
        }

        if (filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
            $host = '[' . $host . ']';
        } elseif ($host && preg_match(static::HOSTNAME_REGEX, $host) !== 1) {
            throw new InvalidArgumentException('Uri host name validation failed');
        }

        return strtolower($host);
    }

    /**
     * Filter Uri port.
     *
     * This method
     *
     * @param int|null $port
     * @return int|null

View on GitHub (pinned to 6040efed04)

Solutions

  1. Default the host: `$host = $headerHost ?? 'localhost'`
  2. Cast scalar values to string before filtering
  3. Use `$uri->withHost((string) $host)` so failures surface as a clear TypeError at the typed boundary

Example fix

// before
$host = UriPartsFilter::filterHost($env['HTTP_HOST'] ?? null); // CLI: null -> throws

// after
$host = UriPartsFilter::filterHost((string) ($env['HTTP_HOST'] ?? 'localhost'));
Defensive patterns

Strategy: type-guard

Validate before calling

$host = $env['HTTP_HOST'] ?? $env['SERVER_NAME'] ?? 'localhost';
if (!is_string($host)) {
    throw new \InvalidArgumentException('host must be a string');
}

Type guard

function isHostString(mixed $value): bool
{
    return is_string($value) && $value !== '';
}

Prevention

When it happens

Trigger: Calling filterHost(null) when a Host header is absent; passing an array from a malformed parse result; feeding `$env['HTTP_HOST']` derived value that was never cast when calling the filter directly.

Common situations: Middleware reading Host/X-Forwarded-Host headers that may be missing on CLI or synthetic requests; building test fixtures where the host field was left null.

Related errors


AI-assisted analysis of getgrav/grav@6040efed04 (2026-08-17). Data as JSON: /api/errors/eb0372da70f8cb21. Report an issue: GitHub.