symfony/symfony · error · IOException

Symbolic link "%s" was created but appears to be broken.

Error message

Symbolic link "%s" was created but appears to be broken.

What it means

Thrown by assets:install's symlink() helper after it successfully calls $filesystem->symlink($originDir, $targetDir) but file_exists($targetDir) still returns false. This indicates the symlink was created but resolves to a non-existent or inaccessible target — a 'broken symlink' — so the command refuses to report success.

Source

Thrown at src/Symfony/Bundle/FrameworkBundle/Command/AssetsInstallCommand.php:231

        }

        return $method;
    }

    /**
     * Creates symbolic link.
     *
     * @throws IOException if link cannot be created
     */
    private function symlink(string $originDir, string $targetDir, bool $relative = false): void
    {
        if ($relative) {
            $this->filesystem->mkdir(\dirname($targetDir));
            $originDir = $this->filesystem->makePathRelative($originDir, realpath(\dirname($targetDir)));
        }
        $this->filesystem->symlink($originDir, $targetDir);
        if (!file_exists($targetDir)) {
            throw new IOException(\sprintf('Symbolic link "%s" was created but appears to be broken.', $targetDir), 0, null, $targetDir);
        }
    }

    /**
     * Copies origin to target.
     */
    private function hardCopy(string $originDir, string $targetDir): string
    {
        $this->filesystem->mkdir($targetDir, 0o777);
        // We use a custom iterator to ignore VCS files
        $this->filesystem->mirror($originDir, $targetDir, Finder::create()->ignoreDotFiles(false)->in($originDir));

        return self::METHOD_COPY;
    }

    private function getPublicDirectory(ContainerInterface $container): string
    {
        $defaultPublicDir = 'public';

View on GitHub (pinned to 698e28026c)

Solutions

  1. Verify the bundle's origin assets directory exists at <BundlePath>/Resources/public or <BundlePath>/public.
  2. On Windows, run the terminal as Administrator or enable Developer Mode for symlink support, or use copy mode (omit --symlink).
  3. On NFS/volume mounts, prefer copy mode: `assets:install public` (no --symlink).
  4. Re-run after fixing the origin dir; remove the broken symlink manually if needed.

Example fix

# before
php bin/console assets:install public --symlink
# -> 'Symbolic link ... appears to be broken'

# after (fall back to copy)
php bin/console assets:install public
# or fix the missing origin: ensure <Bundle>/Resources/public exists
Defensive patterns

Strategy: validation

Validate before calling

// Verify the bundle origin exists before symlinking
foreach ($kernel->getBundles() as $bundle) {
    $origin = is_dir($bundle->getPath().'/Resources/public')
        ? $bundle->getPath().'/Resources/public'
        : $bundle->getPath().'/public';
    if (!is_dir($origin)) { /* skip or warn */ }
}

Prevention

When it happens

Trigger: Running `assets:install --symlink` when the bundle's Resources/public (or public) origin directory was removed after the symlink target computation, the origin path is on a filesystem that doesn't report through file_exists (some Windows/NFS setups), or the symlink target became stale mid-operation.

Common situations: Bundles whose assets directory doesn't exist at the computed origin path. NFS/Vagrant/Docker volume mounts where symlink resolution is unreliable. Windows without symlink privileges (though that usually fails at symlink() not here). Origin dir deleted/renamed after glob.

Related errors


AI-assisted analysis of symfony/symfony@698e28026c (2026-08-06). Data as JSON: /api/errors/3f1643a61572978e. Report an issue: GitHub.