Magento 2 missing bin/magento, lib, dev and setup directories
Pull a Magento 2 project, find bin/magento, lib/, dev/ or setup/ missing, and nothing is wrong with the repo: those directories are deployed out of the magento/magento2-base package by Composer rather than committed. When that deploy step does not run, or runs partially, the files never land and the project looks half-installed.
The fix
In most cases a reinstall of the base package re-triggers the deploy and the missing directories reappear:
composer reinstall magento/magento2-base
That forces Composer to remove and re-add the package, which runs the magento-composer-installer deploy step again and copies the mapped files back into place. Worth running bin/magento afterwards to confirm the CLI is back.
Why it happens
The deploy is handled by magento/magento-composer-installer, and there is a bug in its copy strategy. The Copy::createDelegate() method uses file_exists(), which dereferences symlinks, so when a symlink with a missing target sits at one of the mapped destination paths, the installer reads it as absent, tries to mkdir() over the existing link node and hits a silent File exists error.
The error swallowing is worse. DeployManager::doDeploy() catches the ErrorException but only logs it under -vvv verbosity, so the remaining mappings get skipped with no warning. The install reports success while setup/ and friends are partially or completely empty.
I’ve raised a GitHub issue with the full reproduction. There’s also a longstanding PR against magento-composer-installer that removes the exception suppression so composer install fails loud and early instead of leaving you with a broken but apparently fine install. It has been open for years awaiting review.
If the reinstall doesn’t stick, check for a dangling symlink at the target paths before assuming Composer is at fault.
Found this useful? Everything here is free and stays that way. If it saved you an afternoon, you can buy me a coffee.