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 a

View on GitHub (pinned to 6ffc117740)

Solutions

  1. Run 'composer validate' and 'composer update --lock' to regenerate lock metadata through the normal loader.
  2. Inspect the offending package name in the error and report/fix the upstream package metadata if it is malformed.
  3. If the source is a custom repository, ensure it emits lowercase, spec-compliant package names.
  4. 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

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


AI-assisted analysis of composer/composer@6ffc117740 (2026-08-07). Data as JSON: /api/errors/8be1b642f78c6b67. Report an issue: GitHub.