Stop Robocopy Exit Code 1 from Breaking Your CI/CD Pipeline

beginner🪟 Windows2026-07-24| Windows Server, Windows 10/11, GitHub Actions Runners, Azure DevOps Agents, GitLab Runners (Windows)

Error Message

##[error]Process completed with exit code 1. ROBOCOPY : Exit : 1 (0x01)
#robocopy#windows#ci-cd#batch#exitcode#github-actions

TL;DR: The Quick Fix

Robocopy doesn't play by the usual rules. While most tools use 0 for success and anything else for failure, Robocopy returns 1 when it successfully copies files. Most CI/CD platforms like GitHub Actions or Azure DevOps see that 1 and immediately kill your build.

To fix this, you need to intercept the exit code. If the value is less than 8, tell the pipeline to treat it as a success:

robocopy source destination /options
if %ERRORLEVEL% LEQ 1 exit 0

Why your pipeline is failing

Standard CLI tools are binary: 0 means it worked, and 1 means it broke. CI/CD runners are hardwired to stop the build the moment they see a non-zero exit code. Robocopy, however, was designed for detailed logging using a bitmask system. It uses exit codes to tell you exactly what happened during the sync.

Here is how Robocopy breaks down its return values:

  • 0: No files were copied. The source and destination were already identical.
  • 1: Success. One or more files were successfully moved to the destination.
  • 2: Extra files exist in the destination that aren't in the source.
  • 4: Mismatched files or directories were detected.
  • 8: Failure. Several files could not be copied.
  • 16: Fatal error. This usually means a disk is full, access is denied, or the network path vanished.

Because 1 is greater than 0, your runner sees ROBOCOPY : Exit : 1 (0x01) and assumes the worst. It flags a failure even though your 500MB of production assets just finished uploading perfectly.

Fixing the Exit Code Logic

Method 1: Batch Script Wrapper

Batch scripts are the most common way to trigger Robocopy on Windows runners. If you are using a .bat or .cmd file, add a check immediately after your command. This ensures the script returns a clean 0 to the runner.

robocopy "C:\build\artifacts" "\\deploy\server" /E /Z /R:5 /W:5

:: If the code is between 0 and 7, force it to 0 so the build passes
if %ERRORLEVEL% LSS 8 exit /b 0

Method 2: PowerShell Handling

PowerShell is the default shell for many modern Windows runners. It tracks the last exit code in the $LASTEXITCODE variable. You can reset this value manually to keep the pipeline moving.

robocopy "source" "destination" /MIR
if ($LASTEXITCODE -lt 8) { 
    $global:LASTEXITCODE = 0 
}

This approach is safer for automated deployments. It allows for minor warnings (like exit code 2 for extra files) while still failing if a real error (code 8 or 16) occurs.

Method 3: GitHub Actions YAML Fix

When calling Robocopy directly inside your main.yml, you can chain the commands. This is the cleanest way to handle the logic without creating separate script files.

- name: Deploy via Robocopy
  shell: cmd
  run: |
    robocopy "src" "dest" /E /NP
    if %ERRORLEVEL% LEQ 1 exit 0

How to verify the fix

Check your pipeline logs after applying these changes. You should still see ROBOCOPY : Exit : 1 in the text output, but the step itself will now show a green checkmark.

  • Trigger a manual build.
  • Locate the Robocopy task in the logs.
  • Confirm the pipeline moves to the next step instead of hanging.
  • Spot-check the destination folder to ensure your files—whether it's 10 images or 1,000 DLLs—actually arrived.

Which codes actually matter?

Don't ignore every error. If Robocopy returns 8 or 16, your pipeline should fail. These codes indicate serious issues like Access Denied or Insufficient Disk Space. By using the LSS 8 (Less Than 8) logic, you ignore the helpful "files copied" notices while still catching the errors that would actually break your deployment.

Related Error Notes