{"record":{"id":"9afdca7a0401a6c0","repo":"getgrav/grav","slug":"malformed-gpm-url-package-file","errorCode":null,"errorMessage":"Malformed GPM URL: {$package_file}","messagePattern":"Malformed GPM URL: (.+?)","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"system/src/Grav/Common/GPM/GPM.php","lineNumber":720,"sourceCode":"                return $plugin;\n            }\n        }\n\n        return false;\n    }\n\n    /**\n     * Download the zip package via the URL\n     *\n     * @param string $package_file\n     * @param string $tmp\n     * @return string|null\n     */\n    public static function downloadPackage($package_file, $tmp)\n    {\n        $package = parse_url($package_file);\n        if (!is_array($package)) {\n            throw new \\RuntimeException(\"Malformed GPM URL: {$package_file}\");\n        }\n\n        $filename = Utils::basename($package['path'] ?? '');\n\n        if (Grav::instance()['config']->get('system.gpm.official_gpm_only') && ($package['host'] ?? null) !== 'getgrav.org') {\n            throw new RuntimeException('Only official GPM URLs are allowed. You can modify this behavior in the System configuration.');\n        }\n\n        $output = Response::get($package_file, []);\n\n        if ($output) {\n            Folder::create($tmp);\n            file_put_contents($tmp . DS . $filename, $output);\n            return $tmp . DS . $filename;\n        }\n\n        return null;\n    }","sourceCodeStart":702,"sourceCodeEnd":738,"githubUrl":"https://github.com/getgrav/grav/blob/6040efed04efa69b8209448ed81308e7c24147c2/system/src/Grav/Common/GPM/GPM.php#L702-L738","documentation":"GPM::downloadPackage() (line 720) runs the supplied package URL through parse_url(); when parse_url cannot return an array (i.e. returns false for a seriously malformed URL), Grav throws before making any HTTP request. This guards the package installer against garbage input; note that a mere missing scheme (e.g. 'plugin.zip') still parses and fails later checks instead.","triggerScenarios":"Calling GPM::downloadPackage($package_file, $tmp) with an unparseable URL such as 'http://:80', a string containing control characters, or a raw filename where an absolute URL is expected; install-from-URL flows that pass user/plugin-provided values through without validation.","commonSituations":"Custom admin tools offering 'install from URL' that forward raw input; copy-pasted URLs with invisible whitespace; code that used to pass local paths and broke after the API started requiring URLs.","solutions":["Validate the input first: filter_var($url, FILTER_VALIDATE_URL) and reject non-URLs with a friendly message before calling downloadPackage.","Fix the URL itself — it must be a parseable absolute URL with scheme and host (e.g. https://getgrav.org/...).","If you meant a local file, install it via the local path/extraction route (Installer with an extracted source) rather than the GPM downloader."],"exampleFix":"// before\nGPM::downloadPackage($input, $tmp); // RuntimeException: Malformed GPM URL\n\n// after\nif (!filter_var($input, FILTER_VALIDATE_URL)) {\n    throw new \\InvalidArgumentException('Please provide a valid package URL.');\n}\nGPM::downloadPackage($input, $tmp);","handlingStrategy":"validation","validationCode":"if (!\\is_string($url) || !filter_var($url, FILTER_VALIDATE_URL)) {\n    throw new \\InvalidArgumentException('A valid absolute package URL is required.');\n}\nGPM::downloadPackage($url, $tmp);","typeGuard":"function isDownloadablePackageUrl(mixed $url): bool\n{\n    return \\is_string($url) && (bool) filter_var($url, FILTER_VALIDATE_URL)\n        && \\is_array(parse_url($url));\n}","tryCatchPattern":"try {\n    $path = GPM::downloadPackage($url, $tmp);\n} catch (\\RuntimeException $e) {\n    // report 'invalid package URL' to the user; do not retry the same string\n}","preventionTips":["Trim and validate URLs at the input boundary of any install-from-URL feature.","Reject schemeless strings early instead of relying on downstream GPM checks.","Add UI-side pattern hints so users paste full https:// URLs."],"tags":["gpm","package-management","url","validation","installer"],"backgroundTag":"invalid-url-format","analyzedSha":"6040efed04efa69b8209448ed81308e7c24147c2","analyzedAt":"2026-08-17T05:07:31.593Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}