getgrav/grav · error · InvalidArgumentException

Uri scheme must be a string

Error message

Uri scheme must be a string

What it means

UriPartsFilter::filterScheme() asserts its argument is a string before lowercasing it, throwing InvalidArgumentException otherwise. It is called from AbstractUri::withScheme() and from the Uri parts constructor. Note that withScheme() declares a native `string $scheme` parameter, so through that path PHP itself usually raises a TypeError first; this exception surfaces mainly on direct filterScheme() calls or untyped call sites.

Source

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

use function is_string;

/**
 * Class Uri
 * @package Grav\Framework\Uri
 */
class UriPartsFilter
{
    const HOSTNAME_REGEX = '/^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/u';

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

        return strtolower($scheme);
    }

    /**
     * Filters the user info string.
     *
     * @param string $info The raw user or password.
     * @return string The percent-encoded user or password string.
     * @throws InvalidArgumentException
     */
    public static function filterUserInfo($info)
    {
        if (!is_string($info)) {
            throw new InvalidArgumentException('Uri user info must be a string');
        }

View on GitHub (pinned to 6040efed04)

Solutions

  1. Default the value before passing: `$scheme = $config['scheme'] ?? 'https'`
  2. Cast scalars to string; reject arrays/objects explicitly
  3. Prefer the typed API `$uri->withScheme('https')` over calling the filter directly

Example fix

// before
$scheme = $settings['scheme']; // missing key -> null
$new = $uri->withScheme(UriPartsFilter::filterScheme($scheme));

// after
$scheme = $settings['scheme'] ?? 'https';
$new = $uri->withScheme((string) $scheme);
Defensive patterns

Strategy: type-guard

Validate before calling

$scheme = $config['scheme'] ?? 'https';
if (!is_string($scheme)) {
    throw new \InvalidArgumentException('scheme must be a string');
}

Type guard

function isSchemeString(mixed $value): bool
{
    return is_string($value) && preg_match('/^[a-z][a-z0-9+.-]*$/i', $value) === 1;
}

Prevention

When it happens

Trigger: Calling UriPartsFilter::filterScheme(null) directly; passing a config value that is null/int because the key was missing (`$config->get('scheme')` returning null); building a Uri from a hand-assembled parts array where 'scheme' holds a non-string.

Common situations: Code reading optional scheme settings from YAML/config without a default; arrays built from heterogeneous data passed to Uri::createFromString/UriFactory::createFromParts paths.

Related errors


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