# Error: Build 'qemu' errored: Script exited with non-zero exit status: 4 (Packer)

## TL;DR
A provisioner script failed. The exit code tells you how it failed; the real cause is in the provisioner output just above the error. Read that output, fix the underlying command, and rebuild.

## The error

```
Build 'qemu' errored after 17 minutes 54 seconds: Script exited with non-zero exit status: 4. Allowed exit codes are: [0]
```

## Fix it

1. Scroll up in the build log to the provisioner step. The failing command and its output are there; the exit status line is just the summary.
   - Success check: you find the actual failing command (in the issue: WinRM connection refused).
2. Reproduce the failing command manually if possible (SSH/WinRM into a similar host, or `packer build -on-error=ask` to keep the instance alive for inspection).
   - Success check: you can reproduce outside Packer.
3. Fix the underlying problem (wrong endpoint, missing dependency, bad command), not the exit code.
   - Success check: the command succeeds manually.
4. If the non-zero exit is expected (e.g. a command that returns 4 on success-with-warnings), set `valid_exit_codes` on the provisioner to include it.
   - Success check: the build treats that code as success.

## When to use this
You hit this whenever any shell/powershell/ansible provisioner step fails.

## When NOT to use this
Do not use this for builder errors (SSH, AMI creation). This is specifically provisioner script failure.

## Compatibility
Packer 1.x, all provisioners that run scripts.

## Variants
- `Script exited with non-zero exit status: 1` (generic failure; the most common)
- `Script exited with non-zero exit status: 127` (command not found on the guest)
- `Allowed exit codes are: [0]` always appended

## Root cause
Packer treats any non-zero exit from a provisioner script as a build failure. The status number is the script's exit code; diagnosis requires the script's own output.

## Edge cases
- `-on-error=ask` keeps the failed instance running so you can inspect interactively. Remember to clean it up.
- Ansible provisioner failures wrap the playbook recap; `failed=1` in the recap is the pointer.
