getgrav/grav · error · InvalidArgumentException

Uri query string and fragment must be a string

Error message

Uri query string and fragment must be a string

What it means

UriPartsFilter::filterQueryOrFragment() percent-encodes a query string or fragment and asserts the input is a string, throwing InvalidArgumentException otherwise. One filter serves both withQuery() and withFragment() as well as Grav\Common\Uri's query helper. As with the other filters, the native string parameters on the with*() methods mean this throw appears chiefly on direct calls passing null/array values.

Source

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

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

    /**
     * Filters the query string or fragment of a URI.
     *
     * @param string $query The raw uri query string.
     * @return string The percent-encoded query string.
     * @throws InvalidArgumentException If the query is invalid.
     */
    public static function filterQueryOrFragment($query)
    {
        if (!is_string($query)) {
            throw new InvalidArgumentException('Uri query string and fragment must be a string');
        }

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

View on GitHub (pinned to 6040efed04)

Solutions

  1. Validate the raw parameter: `is_string($q) ? ... : ''`
  2. Build query strings with http_build_query() from arrays rather than filtering raw input
  3. Use the typed `$uri->withQuery($query)` / `withFragment($fragment)` APIs

Example fix

// before
$query = UriPartsFilter::filterQueryOrFragment($_GET['q']); // ?q[]=x -> array -> throws

// after
$raw = $_GET['q'] ?? '';
$query = UriPartsFilter::filterQueryOrFragment(is_string($raw) ? $raw : '');
Defensive patterns

Strategy: type-guard

Validate before calling

$query = $_GET['q'] ?? '';
if (!is_string($query)) {
    $query = ''; // or reject: array syntax (?q[]=) must not reach the filter
}

Type guard

function isQueryStringString(mixed $value): bool
{
    return is_string($value);
}

Prevention

When it happens

Trigger: Calling filterQueryOrFragment() directly with `$_GET['q']` when the client sent `?q[]=1` (array); passing null from an optional fragment; feeding an http_build_query() result that was overwritten by an array variable.

Common situations: Search/query parameters consumed without type validation; array-parameter injection (`?x[]=`) reaching code that filters the raw value.

Related errors


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