Overview
On Windows (especially with Docker Desktop using WSL2), docker build, docker cp, or buildx outputs may fail with:
failed to copy files: copy file range failed: invalid argument
Root cause: the optimized copy syscall (copy_file_range) is not supported or partially supported on some filesystems exposed to Linux, such as Windows drives mounted under /mnt/c (9P/SMB) or network shares. When Docker/BuildKit attempts a fast copy to or from those paths, it may return EINVAL (invalid argument).
Quick fixes
- Build from the WSL2 Linux filesystem, not /mnt/c or network shares.
- If you must use Windows paths, disable BuildKit for that build.
- For buildx outputs, write to a Linux path (e.g., /home/user/out) or use type=tar.
- Avoid bind mounts and docker cp directly to Windows paths during the copy step.
- Update Docker Desktop and WSL to the latest versions.
Minimal working example
This shows the error when building from /mnt/c and the fix when building from the WSL filesystem.
Dockerfile:
# Dockerfile
FROM busybox:latest
WORKDIR /work
COPY hello.txt /work/hello.txt
CMD ["cat", "/work/hello.txt"]
Reproduce from a Windows-mounted path (/mnt/c):
# Inside WSL2 shell
mkdir -p /mnt/c/tmp/demo && cd /mnt/c/tmp/demo
printf "hello\n" > hello.txt
docker build -t demo:bad .
# This may fail with: copy file range failed: invalid argument
Fix by moving to the Linux filesystem (e.g., /home):
# Inside WSL2 shell
mkdir -p ~/demo && cp /mnt/c/tmp/demo/* ~/demo/ && cd ~/demo
docker build -t demo:ok .
# Should succeed
Quickstart: the 80/20 solution
- Move your project into the WSL2 Linux filesystem.
- Location: /home/<user>/project or any path not under /mnt/*.
- From Windows Explorer: use \wsl$<Distro>\home<user>\project.
- Re-run the build with BuildKit on.
- BuildKit works well when both build context and outputs are on Linux paths.
- If you still must touch Windows paths during build/output, use one of:
- For buildx: --output type=tar to a Linux path, then move the tar to Windows afterwards.
- For copy to host: docker cp to a Linux path first, then move via \wsl$.
Step-by-step fixes
- Confirm where your files live
- If your project path starts with /mnt/c, /mnt/d, or a mounted network share, it is likely affected.
- Prefer /home/<user> or another ext4 path in the WSL distro.
- Move project into WSL
# From WSL2
cp -r /mnt/c/path/to/app ~/app
cd ~/app
- Build with BuildKit normally
# Linux shell (WSL2)
docker build -t myimg:latest .
- If you cannot move the project, temporarily disable BuildKit
- Bash (WSL):
DOCKER_BUILDKIT=0 docker build -t myimg:legacy .
- PowerShell:
$env:DOCKER_BUILDKIT=0; docker build -t myimg:legacy .
Note: Disabling BuildKit may reduce build performance and features.
- Adjust buildx outputs
- Avoid writing build outputs directly to Windows paths under /mnt/c.
- Use a Linux path or tar output:
docker buildx build \
--output type=local,dest=$HOME/build-out \
-t myimg:bkx .
# Or create a tar artifact
docker buildx build \
--output type=tar,dest=$HOME/build-out/image.tar \
-t myimg:bkx .
Afterwards, move files to Windows via \wsl$ in Explorer or with cp to /mnt/c once the build is done.
- Avoid problematic bind mounts during COPY/ADD
- In docker-compose.yml or docker run, prefer named volumes or Linux-side bind mounts, not /mnt/c.
- If you must use Windows binds for runtime, still perform builds from Linux paths.
- Update components
- Update Docker Desktop and your WSL kernel. Newer versions improve filesystem interop and fallbacks.
When this error appears
- docker build with COPY/ADD when the build context is under /mnt/c.
- docker buildx with --output=type=local,dest=/mnt/c/... or to a network share.
- docker cp container:/path /mnt/c/... (copy out to Windows path).
Pitfalls and gotchas
- Disabling BuildKit:
- Loses advanced features (mount=type=cache, secrets, inline layer caching).
- May be slower and use more disk.
- CRLF line endings:
- If you move sources between Windows and WSL, scripts may break. Set .gitattributes (e.g., *.sh text eol=lf) or git config core.autocrlf=input in WSL.
- Antivirus and indexing:
- Real-time scanning on Windows paths can slow or interfere with file operations. Building in WSL avoids this.
- File permissions:
- Windows paths may not preserve Unix mode bits as expected. Builds from WSL ext4 are more predictable.
Performance notes
- Build context location matters:
- WSL ext4 (/home) builds are significantly faster and more reliable than /mnt/c.
- Use .dockerignore to reduce context size and IO overhead.
- Prefer buildx cache-export/import and mount=type=cache for dependency caches, but keep paths on Linux FS.
- For large outputs, type=tar creates one stream and is often faster and more robust than many small file copies to Windows paths.
Troubleshooting checklist
- Is your path under /mnt/c? Move it under /home and retry.
- Are you writing outputs to /mnt/c? Change dest to a Linux path or use type=tar.
- Still failing? Temporarily set DOCKER_BUILDKIT=0 and compare.
- Update Docker Desktop and WSL kernel.
- If only docker cp fails, copy to a Linux path first, then move to Windows.
Tiny FAQ
Why does this happen mostly on Windows/WSL2? Windows filesystems are exposed to Linux via protocols that do not fully support copy_file_range, leading to EINVAL.
Will switching to Hyper-V or Windows containers help? It can, but the simplest fix is to keep build context and outputs on the WSL filesystem.
Can I safely keep BuildKit enabled? Yes—when working entirely within WSL paths. Only disable it as a temporary workaround.
Does this affect Linux hosts? Rarely. It can occur on certain network filesystems (CIFS/NFS) with limited syscall support.
How do I access my WSL files from Windows? Use \wsl$<Distro> to browse and move artifacts after the build.