{"record":{"id":"b15e31a5923d4ac5","repo":"composer/composer","slug":"symlink-from-s-to-s-failed","errorCode":null,"errorMessage":"Symlink from \"%s\" to \"%s\" failed!","messagePattern":"Symlink from \"(.+?)\" to \"(.+?)\" failed!","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"src/Composer/Downloader/PathDownloader.php","lineNumber":147,"sourceCode":"                        if (!$this->filesystem->isAbsolutePath($absolutePath)) {\n                            $absolutePath = Platform::getCwd() . DIRECTORY_SEPARATOR . $path;\n                        }\n                        $shortestPath = $this->filesystem->findShortestPath($absolutePath, $realUrl, false, true);\n                        $symfonyFilesystem->symlink($shortestPath.'/', $path);\n                    } else {\n                        $symfonyFilesystem->symlink($realUrl.'/', $path);\n                    }\n                }\n            } catch (IOException $e) {\n                if (in_array(self::STRATEGY_MIRROR, $allowedStrategies, true)) {\n                    if ($output) {\n                        $this->io->writeError('');\n                        $this->io->writeError('    <error>Symlink failed, fallback to use mirroring!</error>');\n                    }\n                    $currentStrategy = self::STRATEGY_MIRROR;\n                    $isFallback = true;\n                } else {\n                    throw new \\RuntimeException(sprintf('Symlink from \"%s\" to \"%s\" failed!', $realUrl, $path));\n                }\n            }\n        }\n\n        // Fallback if symlink failed or if symlink is not allowed for the package\n        if (self::STRATEGY_MIRROR === $currentStrategy) {\n            $realUrl = $this->filesystem->normalizePath($realUrl);\n\n            if ($output) {\n                $this->io->writeError(sprintf('%sMirroring from %s', $isFallback ? '    ' : '', $url), false);\n            }\n            $iterator = new ArchivableFilesFinder($realUrl, []);\n            $symfonyFilesystem->mirror($realUrl, $path, $iterator);\n        }\n\n        if ($output) {\n            $this->io->writeError('');\n        }","sourceCodeStart":129,"sourceCodeEnd":165,"githubUrl":"https://github.com/composer/composer/blob/6ffc1177404d0c50119c22dde6564a380f4a82c9/src/Composer/Downloader/PathDownloader.php#L129-L165","documentation":"Thrown by PathDownloader::install() (line 138-149) when the symlink/junction creation throws a Symfony IOException and the mirror strategy is NOT in the allowed strategies. This happens specifically when the package forces symlink-only mode via transport option symlink:true, so the code cannot fall back to mirroring.","triggerScenarios":"install() selects STRATEGY_SYMLINK, SymfonyFilesystem::symlink() or filesystem->junction() throws IOException, and in_array(STRATEGY_MIRROR, allowedStrategies) is false because symlink:true was set.","commonSituations":"symlink:true forced in the path repo options combined with: insufficient permissions to create symlinks, the install path already exists, Windows path-too-long limits, or antivirus blocking junction creation.","solutions":["Remove the symlink:true transport option so Composer can fall back to mirroring","Grant permissions to create symlinks (on Linux ensure the user can symlink; on Windows enable Developer Mode or run as admin)","Ensure the install path does not already exist before install","On Windows, shorten the path to avoid MAX_PATH limits"],"exampleFix":"// before: forces symlink, no fallback\n\"repositories\": [{\"type\":\"path\",\"url\":\"./pkg\",\"options\":{\"symlink\":true}}]\n// after: allow mirror fallback\n\"repositories\": [{\"type\":\"path\",\"url\":\"./pkg\",\"options\":{\"symlink\":null}}]","handlingStrategy":"fallback","validationCode":"// Allow mirror fallback instead of forcing symlink-only\n$options = $package->getTransportOptions();\nif (($options['symlink'] ?? null) === true && !function_exists('symlink')) {\n    $options['symlink'] = false; // permit mirroring\n    $package->setTransportOptions($options);\n}","typeGuard":null,"tryCatchPattern":"try {\n    $downloader->install($package, $path);\n} catch (\\RuntimeException $e) {\n    if (str_contains($e->getMessage(), 'Symlink from') && str_contains($e->getMessage(), 'failed')) {\n        // relax symlink:true and retry with mirroring\n        $package->setTransportOptions(['symlink' => false] + $package->getTransportOptions());\n        $downloader->install($package, $path);\n    } else { throw $e; }\n}","preventionTips":["Avoid forcing symlink:true unless you control the environment's symlink permissions","On Windows enable Developer Mode or run elevated for symlink/junction rights","Ensure the install path is cleared before install so symlink targets are clean"],"tags":["path-repository","symlink","permissions","windows"],"analyzedSha":"6ffc1177404d0c50119c22dde6564a380f4a82c9","analyzedAt":"2026-08-07T00:01:08.491Z","schemaVersion":2},"datasetVersion":"2026-08-07T02:17:10.218Z"}