{"record":{"id":"c7c46b9ce87750c1","repo":"getgrav/grav","slug":"uri-path-must-be-a-string","errorCode":null,"errorMessage":"Uri path must be a string","messagePattern":"Uri path must be a string","errorType":"validation","errorClass":"InvalidArgumentException","httpStatus":null,"severity":"error","filePath":"system/src/Grav/Framework/Uri/UriPartsFilter.php","lineNumber":110,"sourceCode":"\n        throw new InvalidArgumentException('Uri port must be null or an integer between 0 and 65535');\n    }\n\n    /**\n     * Filter Uri path.\n     *\n     * This method percent-encodes all reserved characters in the provided path string. This method\n     * will NOT double-encode characters that are already percent-encoded.\n     *\n     * @param  string $path The raw uri path.\n     * @return string       The RFC 3986 percent-encoded uri path.\n     * @throws InvalidArgumentException If the path is invalid.\n     * @link   http://www.faqs.org/rfcs/rfc3986.html\n     */\n    public static function filterPath($path)\n    {\n        if (!is_string($path)) {\n            throw new InvalidArgumentException('Uri path must be a string');\n        }\n\n        return preg_replace_callback(\n            '/(?:[^a-zA-Z0-9_\\-\\.~:@&=\\+\\$,\\/;%]+|%(?![A-Fa-f0-9]{2}))/u',\n            fn($match) => rawurlencode((string) $match[0]),\n            $path\n        ) ?? '';\n    }\n\n    /**\n     * Filters the query string or fragment of a URI.\n     *\n     * @param string $query The raw uri query string.\n     * @return string The percent-encoded query string.\n     * @throws InvalidArgumentException If the query is invalid.\n     */\n    public static function filterQueryOrFragment($query)\n    {","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/getgrav/grav/blob/6040efed04efa69b8209448ed81308e7c24147c2/system/src/Grav/Framework/Uri/UriPartsFilter.php#L92-L128","documentation":"UriPartsFilter::filterPath() percent-encodes reserved characters in a URI path (without double-encoding existing sequences) and first asserts the input is a string, throwing InvalidArgumentException otherwise. It backs AbstractUri::withPath(), the Uri parts constructor, and Grav\\Common\\Uri's path helper. Since withPath() declares a native string parameter, the throw mainly fires via direct filterPath() calls or untyped call sites receiving null/array.","triggerScenarios":"Calling filterPath(null) when a route/segment lookup returned null; passing an array produced by a path-explode operation; feeding an object (e.g. Stringable) without casting on direct calls.","commonSituations":"Route builders where an optional path segment config is absent; array_shift() on an already-exhausted segments array yielding null that then flows into the filter.","solutions":["Default to an empty path: `$path = $segments[0] ?? ''`","Cast scalars and reject arrays before building URIs","Prefer the typed `$uri->withPath($path)` boundary"],"exampleFix":"// before\n$path = UriPartsFilter::filterPath($route['path'] ?? null); // null -> throws\n\n// after\n$path = UriPartsFilter::filterPath((string) ($route['path'] ?? ''));","handlingStrategy":"type-guard","validationCode":"$path = $route['path'] ?? '';\nif (!is_string($path)) {\n    throw new \\InvalidArgumentException('path must be a string');\n}","typeGuard":"function isPathString(mixed $value): bool\n{\n    return is_string($value);\n}","tryCatchPattern":null,"preventionTips":["Default optional path segments to '' instead of null","Check array_shift()/optional lookups for null before feeding results to URI builders","Use the typed withPath() API"],"tags":["uri","path","type-check","validation"],"backgroundTag":"type-validation-failed","analyzedSha":"6040efed04efa69b8209448ed81308e7c24147c2","analyzedAt":"2026-08-17T05:07:31.593Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}