composer/composer · critical · SecurityException
Invalid package found during dependency resolution, aborting
Error message
Invalid package found during dependency resolution, aborting: {error} What it means
Thrown as SecurityException by ValidatingArrayLoader::validatePackage() during dependency resolution when a resolved package's name fails the naming check (hasPackageNamingError returns non-null). This re-validates packages that may have been loaded through the non-strict ArrayLoader (e.g. from a lock file or a malicious mirror) to block malicious package names, and aborts resolution immediately.
Source
Thrown at src/Composer/Package/Loader/ValidatingArrayLoader.php:686
* installed from the lock file. This guards against malicious package names and source/dist
* URLs or references that could be interpreted as command-line options (argument injection)
* by the VCS/download tooling.
*
* @throws SecurityException
*/
public static function validatePackage(PackageInterface $package): void
{
// The root package's name/metadata is locally controlled and already validated by
// RootPackageLoader (and its "__root__" placeholder name would be a false positive here).
// RootPackageInterface covers both RootPackage and RootAliasPackage.
if ($package instanceof RootPackageInterface) {
return;
}
// getName() is already lowercased, so the uppercase style branch never fires and only
// structural/security failures throw. Platform packages return null here.
if (null !== ($err = self::hasPackageNamingError($package->getName()))) {
throw new SecurityException('Invalid package found during dependency resolution, aborting: '.$err);
}
// A url or reference starting with a "-" may be misinterpreted as a command-line option
// by the VCS/download tooling, same protection as the source/dist checks done in load().
$sourceDist = [
'source.url' => $package->getSourceUrl(),
'source.reference' => $package->getSourceReference(),
'dist.url' => $package->getDistUrl(),
'dist.reference' => $package->getDistReference(),
];
foreach ($sourceDist as $field => $value) {
if ($value !== null && Preg::isMatch('{^\s*-}', $value)) {
throw new SecurityException($package->getName().' has an invalid '.$field.', it must not start with a "-": '.$value);
}
}
// Bin paths are resolved relative to the package install dir and then chmod'd (and
// proxied) by BinaryInstaller. A ".." segment escapes that directory and lets aView on GitHub (pinned to 6ffc117740)
Solutions
- Run 'composer validate' and 'composer update --lock' to regenerate lock metadata through the normal loader.
- Inspect the offending package name in the error and report/fix the upstream package metadata if it is malformed.
- If the source is a custom repository, ensure it emits lowercase, spec-compliant package names.
- Clear caches ('composer clearcache') and re-resolve to drop any cached malformed metadata.
Example fix
// before (custom repo packages.json emitting bad name)
{ "packages": { "MyVendor/MyPkg": { ... } } }
// after
{ "packages": { "myvendor/my-pkg": { ... } } } Defensive patterns
Strategy: validation
Type guard
use Composer\Package\Loader\ValidatingArrayLoader;
use Composer\Semver\VersionParser;
function packageNameIsValid(string $name): bool
{
return (bool) preg_match('/^[a-z0-9]([_.-]?[a-z0-9]+)*\/[a-z0-9](([_.]|-{0,2})?[a-z0-9]+)*$/', $name);
} Try / catch
use Composer\Package\Loader\ValidatingArrayLoader;
use Composer\Package\Loader\InvalidPackageException;
try {
$loader = new ValidatingArrayLoader(new ArrayLoader(null, true), true);
$package = $loader->load($pkgData);
} catch (InvalidPackageException $e) {
// report $e->getErrors()
} Prevention
- Always lowercase package names and use the vendor/package format.
- Generate lock files with composer update rather than editing them by hand.
- Run 'composer validate --strict' on custom repository metadata.
- Clear caches when switching mirrors.
When it happens
Trigger: Composer resolves a package whose name is structurally invalid — uppercase characters (names must be lowercase lowercase-with-dashes), or otherwise malformed. Because getName() is already lowercased for normal packages, this typically fires for packages loaded bypassing normal validation: a hand-crafted lock file, a tampered provider, or a non-spec-compliant custom repository.
Common situations: A corrupted or tampered composer.lock; a private/custom repository serving non-compliant package metadata; migrating from an old Composer version with looser validation; a package whose name was edited directly in the lock file.
Related errors
- {package} has an invalid {field}, it must not start with a "
- {package} has an invalid bin {bin}, it must not contain ".."
- The package name {value} is invalid, it should be lowercase
- Source URL for policy list "{list name}" must start with "ht
- Invalid fossil identifier detected. Identifier must not star
AI-assisted analysis of composer/composer@6ffc117740 (2026-08-07).
Data as JSON: /api/errors/8be1b642f78c6b67.
Report an issue: GitHub.