composer/composer · error · InvalidArgumentException
Package %s must have a source or dist specified
Error message
Package %s must have a source or dist specified
What it means
Thrown by DownloadManager::getAvailableSources (a private method feeding install/update source selection). It builds a $sources array from sourceType and distType; if both are empty, the package has nothing to download and the InvalidArgumentException fires. Distinguishes from error 232: that is about installationSource being unset, this is about the package lacking source AND dist entirely.
Source
Thrown at src/Composer/Downloader/DownloadManager.php:452
* @return string[]
* @phpstan-return array<'dist'|'source'>&non-empty-array
*/
private function getAvailableSources(PackageInterface $package, ?PackageInterface $prevPackage = null): array
{
$sourceType = $package->getSourceType();
$distType = $package->getDistType();
// add source before dist by default
$sources = [];
if ($sourceType) {
$sources[] = 'source';
}
if ($distType) {
$sources[] = 'dist';
}
if (empty($sources)) {
throw new \InvalidArgumentException('Package '.$package.' must have a source or dist specified');
}
if (
$prevPackage
// if we are updating, we want to keep the same source as the previously installed package (if available in the new one)
&& in_array($prevPackage->getInstallationSource(), $sources, true)
// unless the previous package was stable dist (by default) and the new package is dev, then we allow the new default to take over
&& !(!$prevPackage->isDev() && $prevPackage->getInstallationSource() === 'dist' && $package->isDev())
) {
$prevSource = $prevPackage->getInstallationSource();
usort($sources, static function ($a, $b) use ($prevSource): int {
return $a === $prevSource ? -1 : 1;
});
return $sources;
}
// reverse sources in case dist is the preferred source for this packageView on GitHub (pinned to c435d285c9)
Solutions
- Confirm the package is meant to be installed (not just provided/replaced); if it's virtual, don't install it.
- Ensure the repository serving the package includes a dist or source block in its metadata.
- If the package is yours, publish a tag/release so a dist is generated, or add source info.
- Use 'composer why <pkg>' to verify the dependency isn't pulling a stub package.
Example fix
// before - package.json-style entry with no source/dist
{"name": "vendor/pkg", "version": "1.0.0"}
// after - add a dist
{"name": "vendor/pkg", "version": "1.0.0", "dist": {"url": "https://.../pkg.zip", "type": "zip"}} Defensive patterns
Strategy: validation
Validate before calling
if ($package->getType() !== 'metapackage'
&& empty($package->getSourceType())
&& empty($package->getDistType())) {
throw new InvalidArgumentException('Package ' . $package->getName() . ' has neither source nor dist');
} Type guard
function hasSourceOrDist(\Composer\Package\PackageInterface $p): bool {
return !empty($p->getSourceType()) || !empty($p->getDistType());
} Prevention
- Serve complete dist/source metadata from custom repositories.
- Mark virtual/provide-only packages as metapackage if they aren't installable.
- Publish releases so dist artifacts exist.
When it happens
Trigger: A package declares neither a source (git/svn/hg) nor a dist (zip/tar) — i.e. getSourceType() and getDistType() are both empty/null. Reached during install/update when Composer tries to determine which source(s) to offer.
Common situations: A provider/replace-only or virtual package that was mistakenly treated as installable. A custom repository serving packages without dist/source metadata. A metapackage mishandled upstream of the call.
Related errors
- The given package is missing url information
- Unknown downloader type: %s. Available types: %s.
- Package %s does not have an installation source set
- Package {packageName} is missing reference information
- Package {targetName} is missing reference information
AI-assisted analysis of composer/composer@c435d285c9 (2026-08-07).
Data as JSON: /api/errors/2d5aab1f44e1f4c1.
Report an issue: GitHub.