{"record":{"id":"b6ffb7d7b9c82f0b","repo":"getgrav/grav","slug":"uploaded-archive-file-is-not-a-valid-grav-update-p-b6ffb7","errorCode":null,"errorMessage":"Uploaded archive file is not a valid Grav update package","messagePattern":"Uploaded archive file is not a valid Grav update package","errorType":"console","errorClass":"RuntimeException","httpStatus":null,"severity":"critical","filePath":"system/src/Grav/Console/Gpm/SelfupgradeCommand.php","lineNumber":648,"sourceCode":"\n            $script = $folder . '/system/install.php';\n            if ((file_exists($script) && $install = include $script) && is_callable($install)) {\n                if (is_object($install) && method_exists($install, 'setProgressCallback')) {\n                    $install->setProgressCallback(function (string $stage, string $message, ?int $percent = null, array $extra = []) {\n                        $this->handleServiceProgress($stage, $message, $percent);\n                    });\n                }\n                if (is_object($install) && method_exists($install, 'generatePreflightReport')) {\n                    $report = $install->generatePreflightReport();\n                    if (!$this->handlePreflightReport($report)) {\n                        Installer::setError('Upgrade aborted due to preflight requirements.');\n\n                        return;\n                    }\n                }\n                $install($zip);\n            } else {\n                throw new RuntimeException('Uploaded archive file is not a valid Grav update package');\n            }\n        } catch (\\Throwable $e) {\n            // Catch Throwable (not just Exception) so a PHP Error/TypeError raised\n            // by the bundled installer on a newer PHP runtime still produces a\n            // meaningful message instead of an uncaught fatal with no context.\n            $this->lastThrowable = $e;\n\n            $message = $e->getMessage();\n            if ($message === '') {\n                $message = 'Unexpected ' . get_class($e) . ' during upgrade';\n            }\n            Installer::setError($message);\n\n            $this->logUpgrade('error', 'Exception while staging upgrade: ' . $message, [\n                'exception' => get_class($e),\n                'where' => $e->getFile() . ':' . $e->getLine(),\n                'trace' => $e->getTraceAsString(),\n            ]);","sourceCodeStart":630,"sourceCodeEnd":666,"githubUrl":"https://github.com/getgrav/grav/blob/6040efed04efa69b8209448ed81308e7c24147c2/system/src/Grav/Console/Gpm/SelfupgradeCommand.php#L630-L666","documentation":"SelfupgradeCommand applies a Grav upgrade the same way as direct-install: the freshly downloaded zip must contain system/install.php returning a callable installer. If the archive GPM fetched does not satisfy that contract (file missing, not callable), the command throws this RuntimeException; any Throwable raised by the bundled installer itself is caught and surfaced through Installer::setError with its message preserved.","triggerScenarios":"Running `bin/gpm self-upgrade` to an unstable/prerelease channel or a custom GPM proxy whose package lacks system/install.php; a corrupted/incomplete download served with a 200 status; GPM response pointing at a source-only artifact; upgrading to a version whose installer throws (PHP incompatibility, TypeError) — that path reports the installer's own message instead.","commonSituations":"Self-hosted/private GPM server returning repacked zips without the install script; proxy or CDN mangling the package (truncation, HTML error page saved as zip); testing upgrades to RC builds from GitHub; PHP runtime newer than the bundled installer supports, turning the installer call into an Error.","solutions":["Upgrade to an official stable release: `bin/gpm self-upgrade -f` (or without -f to the latest stable) and confirm the channel in system config is stable.","If you use a custom/remote GPM endpoint, fix the artifacts it serves: each Grav package must contain system/install.php returning a callable, or point back to getgrav.org.","Verify the downloaded package integrity: check the zip in the GPM temp/cache location with `unzip -t` and confirm system/install.php exists inside.","If the reported message comes from the installer throwing, address that root cause (e.g. raise/lower PHP version, fix folder permissions) before retrying the upgrade."],"exampleFix":"# before\n$ bin/gpm self-upgrade   # package from custom proxy without install.php -> error\n\n# after (system.yaml -> gpm: official_endpoint: https://getgrav.org/downloads) \n$ bin/gpm self-upgrade -f","handlingStrategy":"validation","validationCode":"// before self-upgrade, confirm endpoint is official and reachable\n$endpoint = $grav['config']->get('system.gpm.official_endpoint');\nif ($endpoint !== 'https://getgrav.org/downloads') {\n    exit(\"Refusing self-upgrade from unofficial endpoint: {$endpoint}\" . PHP_EOL);\n}\n// then: passthru('php bin/gpm self-upgrade -f');","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep the GPM official endpoint at getgrav.org; scrutinize any private GPM proxy's artifacts.","Test upgrades on a staging copy first when using custom packages.","After a failed self-upgrade, inspect Installer::getError() output (shown by the command) — it carries the installer's real message when the bundled script threw."],"tags":["grav","gpm","self-upgrade","upgrade","package-validation"],"backgroundTag":"invalid-upgrade-package","analyzedSha":"6040efed04efa69b8209448ed81308e7c24147c2","analyzedAt":"2026-08-17T05:07:31.593Z","schemaVersion":2},"datasetVersion":"2026-08-17T09:17:11.063Z"}