Skip to content
Latchkey

Git "destination path already exists and is not an empty directory"

Git refuses to clone into a directory that already has files in it. On a reused or self-hosted runner the previous job left a checkout behind, so the fresh clone has nowhere clean to land.

What this error means

A git clone step fails immediately with fatal: destination path 'repo' already exists and is not an empty directory. It happens on persistent/self-hosted runners that reuse the same workspace, not on ephemeral runners that start clean.

git clone output
fatal: destination path 'app' already exists and is not an empty directory.

Diagnose it: depth, refs, or credentials?

CI checkouts are shallow and detached by default, which breaks anything that needs history or a branch name. Before treating it as a credential problem, confirm what the runner actually fetched.

.github/workflows/ci.yml
- run: |
    git rev-parse --is-shallow-repository
    git rev-parse --abbrev-ref HEAD      # prints HEAD when detached
    git log --oneline -3
    git remote -v
    git for-each-ref --format="%(refname)" | head

Common causes

A leftover checkout from a previous job

Self-hosted or reused runners keep the working directory between jobs. A prior clone is still there, so the new clone target is non-empty and Git aborts.

A cache or artifact restored into the clone path

A cache restore or artifact download placed files at the same path the clone targets, leaving the directory dirty before the clone runs.

How to fix it

Clean the workspace before cloning

Remove or empty the target directory at the start of the job so the clone has a clean landing spot.

Terminal
rm -rf app
git clone https://github.com/org/app.git app

Reset an existing checkout instead of re-cloning

If you want to reuse the directory, fetch and hard-reset rather than cloning over it.

Terminal
cd app
git fetch origin
git reset --hard origin/main
git clean -ffdx

The checkout options that fix most of this

.github/workflows/ci.yml
- uses: actions/checkout@v4
  with:
    fetch-depth: 0        # full history: diffs, tags, git describe
    submodules: recursive # submodules are NOT fetched by default
    persist-credentials: false  # if a later step pushes with its own token

How to prevent it

  • Use ephemeral runners, or clean the workspace at job start on persistent ones.
  • Enable actions/checkout’s clean step (default) so the workspace resets each run.
  • Avoid restoring caches/artifacts into the repository checkout path.

Frequently asked questions

What causes Git "destination path already exists and is not an empty directory"?
There are 2 common causes: a leftover checkout from a previous job and a cache or artifact restored into the clone path. Self-hosted or reused runners keep the working directory between jobs.
How do I fix Git "destination path already exists and is not an empty directory"?
There are 2 fixes depending on which cause you have: clean the workspace before cloning and reset an existing checkout instead of re-cloning. Work through them in order, since the first is the most common.
What does Git "destination path already exists and is not an empty directory" actually mean?
A git clone step fails immediately with fatal: destination path 'repo' already exists and is not an empty directory.
How do I stop Git "destination path already exists and is not an empty directory" happening again?
Use ephemeral runners, or clean the workspace at job start on persistent ones. The prevention section lists 3 changes that keep it from recurring.

Related guides

References

Not every red build is your code. Latchkey repairs the ones that are not, on the runner. Start free → 30-day trial · No credit card