# Actions/Checkout Limitations: 10 Critical Constraints Every GitHub User Must Know

> Uncover 10 critical limitations of actions/checkout, including shallow clones, tag omissions, and security blocks for forked PRs. Avoid common pitfalls in your GitHub Actions workflow.

- Repository: [GitHub Actions/checkout](https://github.com/actions/checkout)
- Tags: deep-dive
- Published: 2026-07-03

---

**The `actions/checkout` GitHub Action enforces shallow clones by default, omits tags and Git LFS objects unless explicitly enabled, blocks forked pull request checkouts without security opt-in, and requires Git version 2.25 or higher for sparse-checkout functionality.**

The `actions/checkout` action is the official GitHub Action for pulling repository code into CI/CD workflows. While it handles standard cloning automatically, the source code in `actions/checkout` reveals several built-in limitations that can trigger build failures when you need full Git history, large files, or access to external repositories.

## Shallow Clone by Default (fetch-depth)

By default, `actions/checkout` performs a shallow clone that retrieves only the single commit that triggered the workflow run. In [`src/input-helper.ts`](https://github.com/actions/checkout/blob/main/src/input-helper.ts) (lines 31-38), the `fetch-depth` input defaults to `1`, meaning your workflow has no access to file history, previous commits, or diff information.

This optimization speeds up checkout times but breaks workflows that rely on `git log`, `git describe`, or semantic versioning tools that scan tags.

```yaml
- uses: actions/checkout@v7
  with:
    fetch-depth: 0  # Fetch complete history instead of single commit

```

## Tags and LFS Objects Omitted by Default

The action excludes **annotated tags** and **Git LFS (Large File Storage)** objects unless you explicitly enable them. According to [`src/input-helper.ts`](https://github.com/actions/checkout/blob/main/src/input-helper.ts) (lines 12-15), `fetch-tags` defaults to `false`, and lines 22-25 show `lfs` also defaults to `false`.

Without these settings, workflows requiring version tags or binary assets stored in LFS will fail with "file not found" or "smudge filter" errors.

```yaml
- uses: actions/checkout@v7
  with:
    fetch-depth: 0
    fetch-tags: true
    lfs: true

```

## Submodule Directories Remain Empty

Submodules are not initialized automatically. The input parsing in [`src/input-helper.ts`](https://github.com/actions/checkout/blob/main/src/input-helper.ts) (lines 26-35) sets `submodules` to `false` by default, leaving submodule directories empty on the runner. You must explicitly request recursive or single-level submodule checkout.

```yaml
- uses: actions/checkout@v7
  with:
    submodules: recursive  # Options: 'true' or 'recursive'

```

## Sparse Checkout Requires Git >= 2.25

The sparse-checkout feature, which allows downloading only specific directories, requires Git version 2.25 or higher. The validation logic in [`src/input-helper.ts`](https://github.com/actions/checkout/blob/main/src/input-helper.ts) (lines 94-104) checks for this version requirement, and the action will fail on older self-hosted runners.

Additionally, sparse-checkout operates in "cone mode" by default (faster, path-pattern matching), but you can disable this for legacy behavior using `sparse-checkout-cone-mode: false`.

```yaml
- uses: actions/checkout@v7
  with:
    sparse-checkout: |
      src/
      docs/
    sparse-checkout-cone-mode: true

```

## Fork PR Checkouts Blocked Without Security Opt-in

For security reasons, the action refuses to checkout code from forked pull requests when running on `pull_request_target` or `workflow_run` events. The enforcement logic in [`src/unsafe-pr-checkout-helper.ts`](https://github.com/actions/checkout/blob/main/src/unsafe-pr-checkout-helper.ts) (lines 13-80) validates the repository origin and blocks the operation unless you explicitly set `allow-unsafe-pr-checkout: true`.

This prevents potential credential leakage when untrusted code executes in privileged workflow contexts.

```yaml
- uses: actions/checkout@v7
  with:
    allow-unsafe-pr-checkout: true  # Only after security review

```

## Credential Persistence Changes

The default value for `persist-credentials` changed from `true` to `false` in recent versions. As defined in [`src/input-helper.ts`](https://github.com/actions/checkout/blob/main/src/input-helper.ts) (lines 49-52), credentials are no longer persisted to the Git config by default. This prevents the GitHub token from being written to disk, but if you need downstream steps to perform authenticated Git operations, you must explicitly enable it.

```yaml
- uses: actions/checkout@v7
  with:
    persist-credentials: true  # Required for subsequent authenticated git commands

```

## Cross-Repository Access Requires PAT

The built-in `github.token` can only read the current repository. To checkout a different private repository, you must provide a Personal Access Token (PAT) with appropriate scopes via the `token` input. The action does not support checking out repositories from arbitrary Git hosts like Bitbucket or GitLab; it only works against GitHub.com or GitHub Enterprise Server (GHES) instances as specified by the `github-server-url` input.

```yaml
- uses: actions/checkout@v7
  with:
    repository: my-org/other-private-repo
    token: ${{ secrets.PAT }}

```

## Runtime and Version Requirements

The action now runs on Node 24 and requires Git version 2.18 or higher for features like safe-directory handling. Version checks in [`src/git-version.ts`](https://github.com/actions/checkout/blob/main/src/git-version.ts) enforce these requirements, meaning older self-hosted runners may fail to execute the action even if they support basic Git operations.

## Summary

- **Shallow clones**: `fetch-depth` defaults to `1`, limiting access to commit history.
- **Missing metadata**: Tags and LFS files require explicit `fetch-tags: true` and `lfs: true` settings.
- **Empty submodules**: Submodule directories populate only when `submodules` is set to `true` or `recursive`.
- **Version constraints**: Sparse-checkout requires Git >= 2.25; safe-directory requires >= 2.18.
- **Security blocks**: Fork PR checkouts require `allow-unsafe-pr-checkout: true` on `pull_request_target`.
- **Credential handling**: `persist-credentials` now defaults to `false` to prevent token leakage.
- **Access restrictions**: Cross-repository and private repo access requires a PAT; platform limited to GitHub/GHES.

## Frequently Asked Questions

### How do I fetch the full Git history in actions/checkout?

Set `fetch-depth: 0` in your workflow configuration. By default, the action only fetches the single commit that triggered the workflow (defined in [`src/input-helper.ts`](https://github.com/actions/checkout/blob/main/src/input-helper.ts)). Setting this to `0` retrieves all history and tags (if combined with `fetch-tags: true`), enabling commands like `git log` and `git describe`.

### Why are my Git LFS files missing after checkout?

The `lfs` input defaults to `false` in [`src/input-helper.ts`](https://github.com/actions/checkout/blob/main/src/input-helper.ts) (lines 22-25). You must explicitly add `lfs: true` to your checkout step. Without this setting, LFS pointers remain as text files instead of downloading the actual binary content.

### Can I checkout code from a forked pull request in a workflow_run event?

Not by default. The action blocks checkouts from forked repositories on `workflow_run` and `pull_request_target` events to prevent credential theft. You must set `allow-unsafe-pr-checkout: true` to override this protection, as implemented in [`src/unsafe-pr-checkout-helper.ts`](https://github.com/actions/checkout/blob/main/src/unsafe-pr-checkout-helper.ts). Only enable this after reviewing the security implications of running untrusted code.

### Does actions/checkout work with GitLab or Bitbucket repositories?

No. The action only supports GitHub.com and GitHub Enterprise Server instances. While you can specify a different server URL using the `github-server-url` input, the underlying authentication and API calls are specific to GitHub's platform. For other Git hosts, use native Git commands or host-specific actions.