composer/composer · warning · RuntimeException

Could not view diff\n\n:".$output

Error message

Could not view diff\n\n:".$output

What it means

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.

Source

Thrown at src/Composer/Downloader/GitDownloader.php:589

    {
        $path = $this->normalizePath($path);
        if (0 !== $this->process->execute(['git', 'stash', '--include-untracked'], $output, $path)) {
            throw new \RuntimeException("Could not stash changes\n\n:".$output);
        }

        $this->hasStashedChanges[$path] = true;

        return \React\Promise\resolve(null);
    }

    /**
     * @throws \RuntimeException
     */
    protected function viewDiff(string $path): void
    {
        $path = $this->normalizePath($path);
        if (0 !== $this->process->execute(['git', 'diff', 'HEAD'], $output, $path)) {
            throw new \RuntimeException("Could not view diff\n\n:".$output);
        }

        $this->io->writeError($output);
    }

    protected function normalizePath(string $path): string
    {
        if (Platform::isWindows() && strlen($path) > 0) {
            $basePath = $path;
            $removed = [];

            while (!is_dir($basePath) && $basePath !== '\\') {
                array_unshift($removed, basename($basePath));
                $basePath = dirname($basePath);
            }

            if ($basePath === '\\') {
                return $path;

View on GitHub (pinned to c435d285c9)

Solutions

  1. Run 'git -C <path> diff HEAD' manually to see the underlying error.
  2. If there is no HEAD yet, complete the initial checkout first.
  3. Repair the index ('git fsck', remove index.lock) and retry.
  4. Choose a different prompt option (e.g. 'v' for the file list) if diff is unavailable.

Example fix

# before: 'Could not view diff' when pressing 'd'
# after
git -C vendor/vendor/pkg diff HEAD   # diagnose
git -C vendor/vendor/pkg fsck --full
composer update vendor/pkg            # retry, press 'v' for file list
Defensive patterns

Strategy: try-catch

Validate before calling

// Only meaningful interactively; ensure HEAD exists before offering 'd'.
exec('git -C ' . escapeshellarg($path) . ' rev-parse --verify HEAD', $out, $rc);
if ($rc !== 0) {
    // skip offering the diff option
}

Try / catch

try {
    $downloader->viewDiff($path);
} catch (\RuntimeException $e) {
    if (str_contains($e->getMessage(), 'Could not view diff')) {
        // diff is informational; degrade gracefully
        return;
    }
    throw $e;
}

Prevention

When it happens

Trigger: 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.

Common situations: A repo with no HEAD yet (fresh clone before checkout); a corrupted index; git binary issues; detached state where HEAD is unreadable.

Related errors


AI-assisted analysis of composer/composer@c435d285c9 (2026-08-07). Data as JSON: /api/errors/9d651e544126af8e. Report an issue: GitHub.