{"record":{"id":"9d651e544126af8e","repo":"composer/composer","slug":"could-not-view-diff-n-n-output","errorCode":null,"errorMessage":"Could not view diff\\n\\n:\".$output","messagePattern":"Could not view diff\\\\n\\\\n:\"\\.\\$output","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"warning","filePath":"src/Composer/Downloader/GitDownloader.php","lineNumber":589,"sourceCode":"    {\n        $path = $this->normalizePath($path);\n        if (0 !== $this->process->execute(['git', 'stash', '--include-untracked'], $output, $path)) {\n            throw new \\RuntimeException(\"Could not stash changes\\n\\n:\".$output);\n        }\n\n        $this->hasStashedChanges[$path] = true;\n\n        return \\React\\Promise\\resolve(null);\n    }\n\n    /**\n     * @throws \\RuntimeException\n     */\n    protected function viewDiff(string $path): void\n    {\n        $path = $this->normalizePath($path);\n        if (0 !== $this->process->execute(['git', 'diff', 'HEAD'], $output, $path)) {\n            throw new \\RuntimeException(\"Could not view diff\\n\\n:\".$output);\n        }\n\n        $this->io->writeError($output);\n    }\n\n    protected function normalizePath(string $path): string\n    {\n        if (Platform::isWindows() && strlen($path) > 0) {\n            $basePath = $path;\n            $removed = [];\n\n            while (!is_dir($basePath) && $basePath !== '\\\\') {\n                array_unshift($removed, basename($basePath));\n                $basePath = dirname($basePath);\n            }\n\n            if ($basePath === '\\\\') {\n                return $path;","sourceCodeStart":571,"sourceCodeEnd":607,"githubUrl":"https://github.com/composer/composer/blob/c435d285c9120efdca35696769c72ea9fdcc0466/src/Composer/Downloader/GitDownloader.php#L571-L607","documentation":"Thrown in viewDiff (src/Composer/Downloader/GitDownloader.php:588) when 'git diff HEAD' exits non-zero. viewDiff is only reached through the interactive cleanChanges prompt option 'd' (view local modifications); it is informational and the failure prevents showing the diff.","triggerScenarios":"User selects 'd' at the cleanChanges prompt; viewDiff($path) runs and $this->process->execute(['git','diff','HEAD'],$output,$path) returns non-zero at line 588.","commonSituations":"A repo with no HEAD yet (fresh clone before checkout); a corrupted index; git binary issues; detached state where HEAD is unreadable.","solutions":["Run 'git -C <path> diff HEAD' manually to see the underlying error.","If there is no HEAD yet, complete the initial checkout first.","Repair the index ('git fsck', remove index.lock) and retry.","Choose a different prompt option (e.g. 'v' for the file list) if diff is unavailable."],"exampleFix":"# before: 'Could not view diff' when pressing 'd'\n# after\ngit -C vendor/vendor/pkg diff HEAD   # diagnose\ngit -C vendor/vendor/pkg fsck --full\ncomposer update vendor/pkg            # retry, press 'v' for file list","handlingStrategy":"try-catch","validationCode":"// Only meaningful interactively; ensure HEAD exists before offering 'd'.\nexec('git -C ' . escapeshellarg($path) . ' rev-parse --verify HEAD', $out, $rc);\nif ($rc !== 0) {\n    // skip offering the diff option\n}","typeGuard":null,"tryCatchPattern":"try {\n    $downloader->viewDiff($path);\n} catch (\\RuntimeException $e) {\n    if (str_contains($e->getMessage(), 'Could not view diff')) {\n        // diff is informational; degrade gracefully\n        return;\n    }\n    throw $e;\n}","preventionTips":["Treat viewDiff as optional diagnostics.","Ensure a HEAD exists (complete the initial checkout) before diffing.","Use the file-list option ('v') if a full diff is unavailable."],"tags":["git","diff","interactive","diagnostics"],"backgroundTag":null,"analyzedSha":"c435d285c9120efdca35696769c72ea9fdcc0466","analyzedAt":"2026-08-07T18:58:23.525Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}