The build failed with “The command exited with code 9009” buried inside an MSB3073 error, and my first reaction was to stare at that number like it meant something specific. It doesn’t, really — MSB3073 is Visual Studio’s generic way of saying “a command your build tried to run failed,” and the actual problem is almost always one line above it in the output, not in the error code itself.
Quick Answer
- MSB3073 means an Exec task (usually a pre-build or post-build event) returned a non-zero exit code
- Expand the Output window, not just the Error List — the real error is in the line right before MSB3073
- Copy the exact failing command and run it manually in a Developer Command Prompt to see the actual error
- Common causes: incorrect file paths, missing directories, locked files, permission issues, or a missing tool
- Exit code 9009 specifically means the command or executable couldn’t be found at all
Why MSB3073 Happens
This error is intentionally generic, because it’s really just MSBuild reporting that something it launched didn’t succeed — the actual cause lives entirely in what that something was trying to do.
Incorrect paths are probably the most common root cause. Pre-build and post-build event commands reference file paths, and if a path is wrong, uses the wrong slash direction, or references a variable like $(IntDir) when $(OutDir) was actually needed, the command fails immediately before it ever does its real job.
Missing files or directories trip this up constantly. If a build event command expects an output folder to already exist and it doesn’t — because this is a fresh clone, a clean build, or a CI environment that doesn’t mirror your local setup — the command fails with a non-zero exit code that MSBuild then reports as MSB3073.
Locked files are a sneaky one. Antivirus software, a previous build process still running, or a test runner holding a file open can all block a command from writing where it needs to, and the resulting failure surfaces here even though the underlying cause has nothing to do with your project file itself.
There’s a specific exit code worth knowing on sight: exit code 9009. That number specifically means the command or executable simply wasn’t found — a typo in the path, a tool that isn’t installed, or a PATH environment variable that doesn’t include what the command needs.
Where This Shows Up Most
Custom build events (pre-build, post-build, or custom targets in .csproj/.targets files) are where this error originates almost every time, since MSBuild’s Exec task is exactly what runs those commands.
CI/CD pipelines surface this more than local builds because CI environments often lack tools, paths, or environment variables that exist on a developer’s machine by default, so a build event that works fine locally can fail immediately in a pipeline.
Projects using external tools like code signing utilities, packaging tools, or custom code generators hit this whenever the tool’s own internal error gets wrapped and reported as a generic MSBuild failure rather than a clear tool-specific message.
Cause Comparison Table
| Symptom | Likely Cause | Fix |
|---|---|---|
| Exit code 9009 | Command/tool not found | Check path, install missing tool, verify PATH |
| Exit code 1 (or other nonzero) with a visible tool error above it | The tool itself failed | Read the tool’s own error output, fix that specifically |
| Works locally, fails in CI | Missing environment setup in CI | Match CI environment to local (paths, tools, variables) |
| Intermittent, not consistent | File lock from antivirus/previous process | Close conflicting process, add antivirus exclusion |
| Fails only on clean build | Missing output directory | Add mkdir or CreateDirectory step before the command |
Step-by-Step Fixes
Step 1: Expand the Output window, not just the Error List. The Error List often just shows the final MSB3073 summary. View > Output, then set the dropdown to Build, and look at the full text — the actual failing command and its exit code are shown right there, usually with more context immediately above the MSB3073 line.
Step 2: Copy the exact command and run it manually. Open a Developer Command Prompt for Visual Studio, paste the exact command MSBuild tried to run (including the full path and arguments), and run it directly. The console output from running it manually is almost always clearer than what gets surfaced through the build system.
Step 3: Check the build event that’s failing. Right-click the project > Properties > Build Events, and look at both Pre-Build and Post-Build event command lines. Compare the paths and variables used there against what actually exists in your project structure.
Step 4: Verify all referenced paths and directories exist. If the command writes to or reads from a specific folder, confirm that folder actually exists at build time. Add an explicit directory creation step before the command if there’s any chance the folder might not exist yet on a fresh build.
Step 5: Check for exit code 9009 specifically. If you see this exact code, the tool or command itself wasn’t found. Verify the full path is correct, the tool is actually installed on this machine, and if you’re relying on PATH, confirm the relevant directory is actually in it for the account running the build.
Step 6: Enable diagnostic-level build logging. Tools > Options > Projects and Solutions > Build and Run, set MSBuild project build output verbosity to Detailed or Diagnostic, then reproduce the build. This gives you a much fuller picture of exactly what ran and in what order before the failure.
Step 7: Check for locked files if the failure is inconsistent. If the same build sometimes succeeds and sometimes fails with no code changes, suspect antivirus software or a leftover process holding a file open. Try closing other instances of the app being built, or temporarily disabling real-time antivirus scanning for the build output folder to confirm.
What Actually Worked For Me
I assumed the error was something wrong with my project file configuration, since that’s usually where I go looking first for build errors. Spent a while checking .csproj settings that had nothing to do with the actual problem. The Output window told me plainly, once I actually expanded it properly instead of just glancing at the Error List, that the post-build event was trying to copy a file from a folder that didn’t exist yet on a clean checkout.
The actual fix was one line — adding a directory creation step before the copy command in the post-build event. Felt almost anticlimactic after how long I’d spent assuming it was something more structurally wrong. That said, I’ve also hit MSB3073 from a genuinely locked file once, where the fix was just closing a leftover debug session that had the output DLL open, and no amount of staring at build events would’ve caught that one without noticing the pattern of “it only fails right after debugging.”
Advanced Fixes and Edge Cases
Use binary logging for large or complex projects. Running the build with the -bl flag creates a binary log that can be opened in the MSBuild Structured Log Viewer, which makes tracing exactly what happened before the failure far easier than scrolling through plain text output on big solutions.
Set the MSBuildDebugEngine environment variable for maximum detail. Setting this to 1 produces the most complete possible logging MSBuild can generate, useful specifically when diagnostic verbosity alone isn’t giving you enough to pin down the cause.
Reproduce the exact CI environment locally if the failure only happens in a pipeline. Match the same base image, user context, and environment variables your CI system uses, since a command that works with your local PATH and installed tools can fail entirely in a more minimal CI environment.
Prevention Tips
Avoid relying on PATH for build event commands where possible — use full explicit paths instead, since PATH differences between developer machines and CI environments are a common source of this exact error. Add explicit directory creation steps before any build event command that writes to a folder that might not exist on a clean checkout. And keep build event commands as simple as possible; complex chained commands make it much harder to isolate which specific step failed when MSB3073 shows up.

FAQ
Does MSB3073 always mean my code has a bug? No. It almost always means a build event or external tool command failed, which is usually unrelated to whether your actual source code compiles correctly.
What does exit code 9009 mean specifically? The command or executable couldn’t be found at all — check the path and confirm the tool is actually installed and accessible.
Why does the same build sometimes work and sometimes fail? Intermittent MSB3073 failures often point to file locks from antivirus software or a leftover process, rather than anything wrong with your project configuration.
Is it safe to just remove the failing build event to make the error go away? Only if you understand what that build event was actually doing. Removing it silences the error but may skip something your build genuinely needs, like a code signing step or file copy.
Editor’s Opinion
the number itself tells you basically nothing, which took me an annoyingly long time to accept. the output window has the actual answer nearly every time, i just wasnt looking in the right pane. if youre stuck on this, stop staring at the error code and go find the line right above it, thats where the real story is.