MSBuild "MSB3027: could not copy ... file is locked" in CI
By Kaveh Alemi·Latchkey
MSB3027 means MSBuild tried to copy an output file but the destination (or source) was held open by another process and the copy exhausted its retries. On a busy runner this is usually a transient lock from a lingering test host, antivirus scan, or a parallel build touching the same file.
What this error means
The build fails with MSB3027 naming the file and the process holding the lock, after several copy retries. It is often intermittent across runs.
dotnet
error MSB3027: Could not copy "obj/App.dll" to "bin/App.dll". Exceeded retry count of 10.
Failed. The file is locked by: "testhost (12345)"
Diagnose it: SDK version and restore first
Most .NET CI failures are an SDK mismatch or a restore that did not happen. global.json pins the SDK, and if the runner does not have that exact version the failure message is about the project rather than the SDK.
Terminal
dotnet --info
cat global.json 2>/dev/null
# restore explicitly so a restore failure is not reported as a build failure
dotnet restore --verbosity normal
dotnet build --no-restore -warnaserror
Common causes
A lingering process holds the output file
A previous test host, a still-running app instance, or a parallel build target keeps the DLL/EXE open, so the copy cannot replace it.
On-runner scanning or indexing locks the file
Antivirus or file indexing on the runner briefly locks freshly written outputs, intermittently failing the copy.
How to fix it
Kill lingering processes and serialize the copy
Ensure prior test/app processes are terminated before the build step.
Avoid two jobs writing the same output directory concurrently.
Re-run the job; the lock is usually gone on a fresh attempt.
Lean on auto-retry for transient locks
Treat a single MSB3027 with no code change as transient and re-run.
On self-healing managed runners (Latchkey), transient file-lock copy failures are retried automatically so a one-off lock does not fail the pipeline.
How to prevent it
Do not run multiple jobs against the same output path on one runner.
Stop background app/test processes before rebuilding.
Run CI on self-healing managed runners that auto-retry transient file-lock failures.
Frequently asked questions
What causes MSBuild "MSB3027: could not copy ... file is locked" in CI?
There are 2 common causes: a lingering process holds the output file and on-runner scanning or indexing locks the file. A previous test host, a still-running app instance, or a parallel build target keeps the DLL/EXE open, so the copy cannot replace it.
How do I fix MSBuild "MSB3027: could not copy ... file is locked" in CI?
There are 2 fixes depending on which cause you have: kill lingering processes and serialize the copy and lean on auto-retry for transient locks. Work through them in order, since the first is the most common.
What does MSBuild "MSB3027: could not copy ... file is locked" in CI actually mean?
The build fails with MSB3027 naming the file and the process holding the lock, after several copy retries.
How do I stop MSBuild "MSB3027: could not copy ... file is locked" in CI happening again?
Do not run multiple jobs against the same output path on one runner. The prevention section lists 3 changes that keep it from recurring.