Fix: Git LFS Smudge Filter Error
Part of: Docker, DevOps & Infrastructure
Quick Answer
Resolve Git LFS smudge filter errors by installing Git LFS, fixing credentials, resetting LFS hooks, and handling bandwidth or storage quota issues.
The Error
You run git clone, git pull, or git checkout on a repository that uses Git LFS and get:
Downloading path/to/largefile.bin (150 MB)
Error downloading object: path/to/largefile.bin (abc1234): Smudge error: Error downloading path/to/largefile.bin (abc1234def5678...): batch response: This repository is over its data quota. Account responsible for LFS bandwidth should purchase more data packs to restore access.
Encountered 1 file that should have been a pointer, but wasn't.
error: external filter 'git-lfs smudge -- %f' failed
fatal: path/to/largefile.bin: smudge filter lfs failedOr a shorter variant:
error: external filter 'git-lfs smudge -- %f' failed
fatal: largefile.psd: smudge filter lfs failed
warning: Clone succeeded, but checkout failed.Why This Happens
Git LFS (Large File Storage) works by replacing large files in your repository with small pointer files. When you check out a branch, a smudge filter runs automatically to download the actual large file content from the LFS server and replace the pointer with the real file. When this process fails for any reason, you get a smudge filter error.
Common causes:
- Git LFS is not installed. The
.gitattributesfile in the repository tells Git to use LFS filters, but thegit-lfsbinary is not on your system. Git tries to run a filter that does not exist. - LFS hooks are not initialized. Even if the binary is installed, running
git lfs installsets up the global Git hooks and filter configuration. Without this step, Git does not know how to invoke the smudge filter. - Authentication failure. The LFS server requires credentials and your stored credentials are missing, expired, or incorrect. This often happens after rotating personal access tokens or SSH keys.
- Bandwidth or storage quota exceeded. GitHub, GitLab, and Bitbucket impose limits on LFS storage and bandwidth. Once exceeded, the server rejects download requests.
- Corrupted LFS pointer files. Someone committed the actual binary content instead of a pointer file, or a pointer file got mangled during a merge conflict.
- Network issues or self-hosted LFS server downtime. The LFS endpoint is unreachable, returning HTTP errors, or timing out.
- CI/CD environment missing LFS. Build runners and containers often do not include Git LFS by default, so clones succeed but checkouts fail when the smudge filter runs.
Platform and Environment Differences
The same smudge filter error has very different fixes depending on where you encounter it.
Linux (Debian / Ubuntu / Fedora / Arch). Git LFS ships as a separate package and is rarely installed by default. sudo apt install git-lfs on Debian/Ubuntu, sudo dnf install git-lfs on Fedora, and sudo pacman -S git-lfs on Arch. After installation, git lfs install writes the filter configuration to ~/.gitconfig. The version in distro repos sometimes lags upstream by a year or more, which matters if you hit a smudge bug fixed in a later release — in that case, use the official APT/RPM repo at packagecloud.io/github/git-lfs.
macOS. Homebrew is the standard path: brew install git-lfs. Apple’s preinstalled Git in /usr/bin/git is recent enough to work with LFS, but on Apple Silicon you should verify which Git you are actually running — which git should point at /opt/homebrew/bin/git if you installed via Homebrew, otherwise Xcode Command Line Tools’ Git is used and its filter behavior is occasionally a release behind. Apple’s notarization wrapper can also delay the first git-lfs invocation by several seconds while Gatekeeper scans the binary; the install hangs briefly but is not failing.
Windows native (Git for Windows). The Git for Windows installer bundles Git LFS as an opt-in component on the install wizard’s component-selection page. If you installed without checking that box, git lfs version says “command not found” even though Git itself works. Re-run the installer or download Git LFS separately from git-lfs.com. Chocolatey users can choco install git-lfs, and winget install GitHub.GitLFS also works on recent Windows builds. The smudge filter on Windows is more sensitive to long path names than on Linux — LFS pointer files inside a deeply nested node_modules can fail to expand if Windows long-path support is not enabled.
WSL2. This is the most confusing environment. Inside WSL2 you have a Linux Git, and on the Windows host you have Git for Windows. Each has its own Git LFS configuration. If you git clone from PowerShell and then cd into the same directory from WSL2, the smudge filter set by Git for Windows is recorded in the repo-local .git/config, but the git-lfs binary on the WSL2 $PATH may be missing — so the same checkout that worked from PowerShell fails from WSL2. Install Git LFS inside the WSL2 distro separately and run git lfs install again there. Also avoid checking out repositories under /mnt/c/; the cross-filesystem boundary slows LFS downloads dramatically and occasionally truncates writes, leaving pointer files in a half-expanded state.
Docker. Most lightweight base images (alpine, debian:slim, ubuntu:24.04) do not include Git LFS. Add it explicitly: RUN apt-get update && apt-get install -y git-lfs && git lfs install --system. The --system flag writes the filter configuration to /etc/gitconfig so it applies to all users in the container, not just root. Without it, a multi-stage build that switches to a non-root user can fail to find the LFS filter.
Hosting providers — file size and bandwidth limits. GitHub allows individual LFS files up to 2 GB and grants 1 GB of bandwidth per repo per month on the free tier. GitLab.com allows files up to 5 GB and 10 GB of bandwidth on the free tier. Bitbucket Cloud caps individual files at 5 GB. Self-hosted Git servers (Gitea, Gitlab CE, your own LFS server) typically have no built-in size limit — the cap is your storage backend. The smudge error that says “this repository is over its data quota” only ever comes from a hosted provider; if you see it from a self-hosted server, it is your reverse proxy or upstream object store rejecting the request, not LFS itself.
CI runners. Each CI system handles LFS differently. GitHub Actions’ actions/checkout does not fetch LFS unless you pass lfs: true. GitLab CI shallow-clones by default and skips LFS entirely; set GIT_DEPTH: 0 and add git lfs pull to before_script, or use the lfs strategy in your runner config. CircleCI’s checkout step skips LFS; add a git lfs install && git lfs pull step explicitly. Jenkins’ Git plugin has an “Use Git LFS to pull large files” option in the additional behaviours menu that you must enable per job. Treat “LFS works locally but fails in CI” as the default state and configure LFS into every pipeline.
Fix 1: Install Git LFS
The most common cause is simply not having Git LFS installed. Check first:
git lfs versionIf this returns “command not found,” install it:
macOS:
brew install git-lfsUbuntu/Debian:
sudo apt-get install git-lfsWindows:
Download the installer from git-lfs.com or use:
choco install git-lfsAfter installation, initialize the hooks globally:
git lfs installThis writes the necessary smudge and clean filter configuration to your global ~/.gitconfig. Then re-pull the LFS objects:
git lfs pullFix 2: Re-initialize LFS Hooks
If Git LFS is installed but the filters are not configured (for example, after a Git upgrade or profile reset), re-initialize:
git lfs install --forceThe --force flag overwrites any existing hook configuration. You can verify the filters are set up correctly by checking your Git config:
git config --global --list | grep filter.lfsYou should see output like:
filter.lfs.smudge=git-lfs smudge -- %f
filter.lfs.process=git-lfs filter-process
filter.lfs.clean=git-lfs clean -- %f
filter.lfs.required=trueIf any of these lines are missing, git lfs install did not complete properly. Run it again and check for error messages.
Fix 3: Pull LFS Objects Manually
If the clone completed but checkout failed (you see the “Clone succeeded, but checkout failed” warning), the repository is on disk but the LFS files are still pointer stubs. Pull them manually:
git lfs pullIf that fails, try fetching first and then checking out:
git lfs fetch --all
git checkout -- .The fetch command downloads LFS objects to your local cache without modifying the working tree. The checkout then replaces pointer files with the real content from the cache.
Fix 4: Fix Credential and Authentication Issues
LFS downloads use your Git credentials. If authentication is failing, the smudge filter gets a 401 or 403 response and fails. First, test your connection:
git lfs envThis prints the LFS endpoint URL, authentication method, and other diagnostic information. Look at the Endpoint line to see which server is being contacted.
For HTTPS remotes, make sure your credential helper is configured and your token is valid:
git config --global credential.helper storeThen trigger a credential prompt by pulling:
git lfs pullEnter your username and personal access token when prompted. For GitHub, use a personal access token with the repo scope, not your account password.
For SSH remotes, the issue might be with your SSH key. If your regular Git operations work but LFS fails, note that LFS often uses HTTPS even when the repo remote is SSH. Check the LFS endpoint:
git lfs env | grep EndpointIf it shows an HTTPS URL, your HTTPS credentials need to be valid even though you clone over SSH. You can force LFS to use SSH by setting:
git config lfs.url "[email protected]:user/repo.git/info/lfs"If you are running into SSH key issues in general, see our guide on fixing git permission denied (publickey) errors.
Pro Tip: If your LFS credentials keep expiring in a corporate environment, use a credential manager that caches tokens. On macOS,
git config --global credential.helper osxkeychainstores credentials in the system keychain. On Windows,git config --global credential.helper manager-coreuses the built-in Windows Credential Manager. This prevents smudge filter failures caused by expired session tokens.
Fix 5: Fix .gitattributes Configuration
The .gitattributes file tells Git which files should be tracked by LFS. If it is misconfigured or missing, you can get smudge errors. Check what your repository tracks:
git lfs trackThis lists the patterns from .gitattributes. A typical configuration looks like:
*.psd filter=lfs diff=lfs merge=lfs -text
*.zip filter=lfs diff=lfs merge=lfs -text
*.bin filter=lfs diff=lfs merge=lfs -textIf a file type is listed in .gitattributes but was committed directly to Git (not through LFS), you get a pointer mismatch. To find these files:
git lfs fsckThis scans for files that should be pointers but are not. If it finds problems, you need to fix them by re-tracking the files:
git rm --cached path/to/largefile.bin
git add path/to/largefile.bin
git commit -m "fix: re-track file through LFS"If you want to remove a file pattern from LFS tracking entirely:
git lfs untrack "*.bin"Then update .gitattributes accordingly.
Fix 6: Handle LFS Bandwidth and Storage Quota Limits
GitHub provides 1 GB of free LFS storage and 1 GB of monthly bandwidth per repository. GitLab provides 5 GB. When you exceed these limits, the server rejects LFS downloads and the smudge filter fails with a quota error.
Check your usage on GitHub at Settings > Billing > Git LFS Data. If you are over the limit, your options are:
- Purchase additional data packs. GitHub sells them in 50 GB increments.
- Reduce LFS usage. Remove large files that no longer need to be tracked:
git lfs migrate export --include="*.psd" --everythingThis rewrites history to remove the specified file types from LFS, converting them back to regular Git objects. Be careful: this rewrites commit hashes and requires a force push.
- Use a different LFS server. You can point your LFS storage to a self-hosted server or S3-compatible storage:
git config lfs.url "https://your-lfs-server.example.com/user/repo"Common Mistake: Deleting large files from the repository does not free LFS quota. LFS objects are stored by content hash, and old objects remain on the server until they are garbage collected. To actually reclaim space, you need to delete the files from all branches and tags, then contact your hosting provider to run garbage collection (GitHub does this automatically after a period, but GitLab may require manual intervention).
Fix 7: Skip Smudge Filters During Clone
If you do not need the actual large files immediately (for example, you only need the source code), you can clone without downloading LFS content:
GIT_LFS_SKIP_SMUDGE=1 git clone https://github.com/user/repo.gitThis clones the repository with LFS pointer files instead of the actual content. The pointer files are small text files that look like:
version https://git-lfs.github.com/spec/v1
oid sha256:4d7a214614ab2935c943f9e0ff69d22eadbb8f32b1258daaa5e2ca24d17e2393
size 12345678Later, when you need specific files, pull them selectively:
git lfs pull --include="assets/textures/*"Or pull everything:
git lfs pullThis approach is especially useful when cloning large repositories where you only work on a subset of the LFS-tracked files. Game development repositories, for example, often contain gigabytes of textures and audio assets that a backend developer never needs. Skipping the smudge filter during clone saves significant time and bandwidth, and you can always pull individual directories on demand.
You can also make this the default behavior for a specific repository by configuring it locally:
git config lfs.fetchexclude "assets/audio/*,assets/video/*"This tells LFS to skip downloading files matching those patterns during normal operations. When you eventually need them, git lfs pull --include="assets/audio/*" overrides the exclusion for that one command.
Fix 8: Set Up Git LFS in CI/CD Pipelines
CI/CD runners often do not have Git LFS installed, or they use shallow clones that skip LFS objects. This is a frequent source of smudge filter errors in automated builds.
GitHub Actions:
The default actions/checkout action does not fetch LFS objects. Enable it explicitly:
- uses: actions/checkout@v4
with:
lfs: trueIf you use a self-hosted runner, make sure git-lfs is installed on the runner machine.
GitLab CI:
Add LFS installation to your before_script:
before_script:
- apt-get update && apt-get install -y git-lfs
- git lfs install
- git lfs pullOr, if your GitLab runner supports it, set the GIT_LFS_SKIP_SMUDGE variable to skip LFS during clone and pull selectively:
variables:
GIT_LFS_SKIP_SMUDGE: "1"
build:
script:
- git lfs pull --include="assets/required/*"
- make buildJenkins:
In your Jenkinsfile, add an LFS pull step:
stage('Checkout') {
steps {
checkout scm
sh 'git lfs install'
sh 'git lfs pull'
}
}If your CI build fails with exit code 1 during checkout, see our guide on fixing GitHub Actions exit code failures for general troubleshooting steps.
Fix 9: Fix Corrupted LFS Pointer Files
Sometimes LFS pointer files get corrupted during merge conflicts or when someone commits the actual binary content without LFS installed. Git then tries to run the smudge filter on content that is not a valid pointer, causing the error.
Detect corrupted pointers:
git lfs fsckIf you see errors like “pointer file error” or “object missing,” the pointer files need to be repaired. First, check which files are affected:
git lfs ls-filesFor each corrupted file, reset it:
git checkout origin/main -- path/to/corrupted-file.binIf the file was committed directly (not as a pointer), you need to re-track it through LFS. Remove it from Git’s index, then add it back:
git rm --cached path/to/file.bin
git add path/to/file.bin
git commit -m "fix: re-add file through LFS"To migrate existing large files that were accidentally committed directly into the repository, use:
git lfs migrate import --include="*.bin" --everythingThis rewrites history so that all .bin files across all branches are stored through LFS. After migrating, you will need to force push all affected branches. Coordinate with your team before doing this, since it rewrites commit hashes (similar to how a rebase changes history — if you run into issues with that, our guide on fixing rebase conflicts covers the fundamentals).
Fix 10: Configure a Self-Hosted LFS Server
If you are using a self-hosted Git server or want to store LFS objects on your own infrastructure, you need to configure the LFS endpoint. The smudge filter will fail if it points to the wrong server.
Set the LFS URL for your repository:
git config lfs.url "https://lfs.yourcompany.com/repo"For S3-compatible storage, you can use a transfer agent like lfs-s3:
git config lfs.customtransfer.s3.path /usr/local/bin/lfs-s3
git config lfs.customtransfer.s3.args ""
git config lfs.standalonetransferagent s3Verify your LFS configuration:
git lfs envCheck the Endpoint line. If it shows (auth=none) and your server requires authentication, add credentials:
git config lfs.url "https://username:[email protected]/repo"Or better, use a credential helper so you are not storing tokens in Git config:
git config --global credential.https://lfs.yourcompany.com.helper storeIf your self-hosted server uses a TLS certificate that is not in the system trust store, you may need to specify the CA bundle:
git config http.https://lfs.yourcompany.com.sslCAInfo /path/to/ca-bundle.crtFix 11: Understand Smudge vs Clean Filters
Knowing how LFS filters work helps you diagnose unusual failures. Git LFS uses two filters:
- Clean filter (
git-lfs clean): Runs when yougit adda file. It uploads the large file content to the LFS server and replaces the file in Git’s index with a small pointer file. - Smudge filter (
git-lfs smudge): Runs when yougit checkouta file. It reads the pointer file, downloads the actual content from the LFS server, and replaces the pointer with the real file.
The smudge filter error means the download step failed. You can run the smudge filter manually to see the detailed error:
git lfs smudge < path/to/pointer-fileOr check what LFS would do without actually running the filter:
git lfs pointer --check --file path/to/fileThis tells you whether the file is a valid LFS pointer, the actual content, or something corrupted.
You can also debug filter operations by enabling verbose logging:
GIT_TRACE=1 GIT_LFS_LOG_FILE=lfs-debug.log git checkout -- path/to/fileThen inspect lfs-debug.log for detailed error messages. This is particularly helpful when the error message from Git itself is generic or truncated.
Fix 12: Reset and Re-fetch All LFS Objects
When nothing else works, a full reset of LFS objects often resolves stubborn issues. This is the nuclear option that re-downloads everything. It is useful when LFS objects in your local cache have become corrupted (for example, after a disk error or a power failure during a download) or when you suspect the local cache is out of sync with the remote:
# Clear the local LFS cache
rm -rf .git/lfs/objects
# Re-fetch all LFS objects for the current branch
git lfs fetch
# Replace pointer files with actual content
git lfs checkoutIf you need objects from all branches (not just the current one):
git lfs fetch --allIf the repository is in a broken state after a failed clone, it may be easier to start fresh. Clone with smudge filters disabled, then pull the LFS objects:
rm -rf repo
GIT_LFS_SKIP_SMUDGE=1 git clone https://github.com/user/repo.git
cd repo
git lfs pullThis separates the Git clone from the LFS download, making it easier to diagnose which step is failing. If the clone succeeds but git lfs pull fails, the problem is with LFS server connectivity or credentials, not with Git itself.
Still Not Working?
LFS lock conflicts
If you are using LFS file locking and get smudge errors alongside lock-related messages, check the lock state:
git lfs locksUnlock files that are blocking your checkout:
git lfs unlock path/to/file.bin --forceProxy or firewall blocking LFS
Corporate proxies sometimes block LFS traffic because it uses different endpoints than regular Git operations. Configure Git to use your proxy:
git config --global http.proxy http://proxy.company.com:8080
git config --global https.proxy https://proxy.company.com:8080If only LFS is blocked, set the proxy for the LFS endpoint specifically:
git config http.https://lfs.github.com.proxy http://proxy.company.com:8080Stale Git index after switching branches
Sometimes the smudge error appears after switching between branches that have different .gitattributes configurations. One branch tracks *.bin with LFS, another does not. Switching between them can leave the Git index in an inconsistent state. Clear it and re-checkout:
git rm --cached -r .
git reset --hard HEAD
git lfs pullThis forces Git to re-read .gitattributes and apply the correct filters to all files. If the git reset step gives you merge conflict warnings, you may want to look at our guide on resolving merge conflicts.
Antivirus or security software interference
Some endpoint security tools intercept and scan large file downloads, causing the LFS transfer to time out or fail. If you suspect this, check your security software logs and add an exception for the git-lfs process.
Git version incompatibility
Older versions of Git may not support the LFS filter protocol correctly. Update Git to the latest version:
git --version
git lfs versionUse Git 2.30+ and Git LFS 3.0+ for the best compatibility. Older combinations can have subtle bugs in filter process handling that cause intermittent smudge failures. You can install the latest Git from git-scm.com and the latest LFS from git-lfs.com.
Verify your SSH setup
If LFS uses SSH for transfers and you recently changed your SSH configuration, verify your keys are loaded:
ssh-add -l
ssh -T [email protected]For detailed SSH troubleshooting, see our guide to fixing SSH publickey errors.
WSL2 / Git for Windows hook conflict
If a repository was cloned by Git for Windows and then accessed from WSL2 (or vice versa), the repo-local .git/hooks/ contains hooks installed by the wrong Git. The pre-push hook in particular references the host’s git-lfs binary by absolute path. Remove the repo-local hooks and reinstall LFS from inside whichever environment you are currently using:
rm .git/hooks/pre-push .git/hooks/post-checkout .git/hooks/post-commit .git/hooks/post-merge
git lfs install --local --forceContainer without git-lfs installed at runtime
A common pattern in CI is a multi-stage Dockerfile where the builder stage clones the repo (with LFS) and a smaller runtime stage copies the result. If the runtime stage runs any git command (for build version stamping, for example) and the repo on disk still has LFS pointer files, the smudge filter fails because the runtime stage has no git-lfs binary. Either expand all LFS files in the builder stage with git lfs pull before COPY, or install git-lfs in the runtime stage too.
Files committed before LFS was enabled
If a binary file was committed normally and .gitattributes was added later to track it through LFS, the file in history still exists as a regular Git blob — not an LFS pointer — and Git tries to run the smudge filter on the binary content directly. git lfs migrate import --include="*.bin" --everything rewrites history to move those files into LFS retroactively. Coordinate with your team before doing this; it changes commit hashes for every branch that touches the file.
Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.
Was this article helpful?
Related Articles
Fix: Git Worktree Not Working — Branch Already Checked Out, Prune, Submodules, and Locked Worktrees
How to fix git worktree errors — fatal: 'branch' is already checked out at, worktree prune removing valid trees, detached HEAD on add, submodules not initialized, moving/locking worktrees, and ignoring per-worktree files.
Fix: Undo git reset --hard and Recover Lost Commits
How to undo git reset --hard and recover lost commits using git reflog — step-by-step recovery for accidentally reset branches, lost work, and dropped stashes.
Fix: .gitignore Not Working (Files Still Being Tracked)
How to fix .gitignore not working — files still showing in git status after being added to .gitignore, caused by already-tracked files, wrong syntax, nested gitignore rules, and cache issues.
Fix: git fatal: A branch named 'x' already exists
How to fix 'git fatal: A branch named already exists' when creating or renaming branches — including local conflicts, remote tracking branches, and worktree issues.