Reference
Troubleshooting
Fix setup problems, unavailable machines, queued jobs, and unexpected fallback runs.
My repository is missing or unavailable
- Select the repository’s GitHub account in Mirage’s account menu.
- In Settings, use Manage access on GitHub and confirm the repository is selected for the Mirage App.
- Return to Mirage and select Sync repositories or Sync repository details.
- Confirm it is a private, unarchived repository. Public repositories cannot use Mirage execution.
- Use Retry provisioning if setup reports an error. Private repositories selected in GitHub do not need a separate Mirage switch.
If you cannot change these settings, ask an owner of the GitHub organization. Mirage roles follow GitHub: only organization owners manage settings.
My Mac is connected but not ready
Open the Mac in Machines and check the individual runner profile. A connection means Mirage can reach the host; it does not mean every runner environment is ready.
| What to check | What to do |
|---|---|
| Image download or preparation | Wait for preparation and its self-test. If it fails, read the profile’s error and Next step. |
| Insufficient disk or memory | Free space or reduce the configured runner resources. Image preparation needs room beyond the job’s writable disk budget. |
| Paused, on battery, or under load | Resume the host, connect power, and allow resource pressure to settle. |
| Linux x64 unavailable | Check that Rosetta is installed and the x64 self-test passes. |
| macOS unavailable | Check that the profile is enabled and its image is prepared. Older GUI-bridge installations need the current signed host package; pause and drain before rerunning bootstrap. |
| Offline after reboot | Check the network and power. FileVault may need a person to unlock the disk; native macOS jobs also need a desktop login. |
Use mirage host doctor on the Mac for the detailed checks. See Use the CLI if
the command is not found.
My job stays queued
GitHub shows a waiting job as queued without a reason. Mirage combines its reports in one Mirage check per commit. In the repository's GitHub check setting, choose Always, After 5 minutes, After 15 minutes (the default), Custom, or Never. The delay controls the first waiting report; infrastructure failures appear immediately unless reporting is Never.
The check explains why jobs haven't started, what to do, and what Mirage does when the repository's queue timeout ends. Workflows, matrix jobs and reruns update the same check. Waiting completes as neutral once no job is waiting, even if jobs are now running. Source infrastructure failures report failure; superseded attempts stay in history. Its details link opens Runs in Mirage.
If the check doesn’t explain the wait:
- Confirm GitHub App access and repository provisioning finished.
- Compare the workflow’s
runs-onlabel with the supported runner labels. - Check Machines for a machine available to the selected account. Confirm the required profile is Ready and has headroom for the job’s allocation.
- Check the machine’s Available to list and any capacity grant needed by the account.
- Check GitHub for an Actions outage and confirm Mirage’s dashboard is reachable.
If you need to unblock CI while Mirage is unavailable, restore the job’s original GitHub-hosted label and push the workflow. For a workflow already using the attempt expression, cancelling and manually rerunning it in GitHub selects the hosted label on the next attempt. Check for side effects before rerunning a workflow.
My job ran on GitHub instead of my Mac
Open the job in Mirage and inspect its route reason and attempt number. Common causes are:
- No machine took the job within the repository’s queue timeout: the Mac was busy, paused, asleep, on battery, or short of resources. Check or change the timeout on the repository’s page in Repos.
- The required runner profile was not ready or startup failed.
- The global emergency switch was active, the repository was disconnected or ineligible, or the event did not meet Mirage's trust checks.
- The workflow was manually rerun and its attempt expression selected GitHub.
Fix the reported cause and trigger a new workflow run to try local execution again. Re-running an existing run keeps its later attempt number and will still select the hosted fallback with the documented expression.
My job failed after waiting for a machine
A job on a literal mirage-* label has no fallback. When no machine takes it within the
repository’s queue timeout, Mirage adds a failed
Mirage check that says how long it waited, then cancels the run. Open the check’s
details for the explanation, next steps, and links to the workflow and machines. To fix
it:
- Make a machine available. In Machines, check for one whose profile is Ready, that isn’t paused, and that lists the account under Available to.
- If jobs often wait, add a machine, increase its background work budget, or request smaller runners.
- If long waits are expected, raise the timeout on the repository’s page in Repos.
- If the job should run on GitHub instead, use the attempt expression so it falls back.
Rerun the workflow once a machine can take the job.
Fallback did not run
Check for a literal runs-on: mirage-* label. A literal label targets Mirage on every
attempt. For hosted fallback, use the full expression:
runs-on: ${{ github.run_attempt == '1' && 'mirage-linux-x64' || 'ubuntu-24.04' }}Automatic fallback also requires the GitHub App to retain access and Mirage to be reachable to request the rerun. Direct Cloudflare jobs do not have automatic fallback. A normal failed test does not trigger fallback either; inspect its logs in GitHub.
If attempt 2 does not start on GitHub’s runner, check whether a spending limit or budget is blocking GitHub-hosted runners. See When GitHub-hosted runners are blocked.
My job ran out of disk
A step that fails with No space left on device usually means the runner’s disk filled up,
not that your code is wrong. When a Linux job on a Mac fails with its disk at least 97% full,
Mirage says so: Runs shows Out of disk beside the job, and a failed
Mirage check on the commit says how much of the disk the job used and where it
failed.
Each Linux job on a Mac gets its own disk, the size of the job’s disk allocation (6 GiB unless you change it). The runner image and a swap file sit on that disk too, so your steps have less than the budget. The job’s Disk panel in Runs shows its peak use against the disk.
To fix it:
- Raise the job’s disk allocation under Job routes in Repos. The Mac needs that much free space for each job it runs at once, within its background work budget.
- Or have the job write less: delete build outputs and caches it no longer needs, and prune Docker images it pulled.
- For a Cloudflare job, choose a larger runner size.
To rerun these jobs elsewhere instead, open the repository in Repos and set When
a job runs out of disk to Fall back. Mirage then reruns the job once on its fallback
runner rather than reporting it. A job a setup pull request routes reruns on its original
runner; a job using the attempt expression reruns its
whole workflow, whose next attempt runs on GitHub. A job with a literal mirage-* label has
no fallback, so Mirage reports it either way, as it does when the attempt that ran out of
disk already was the fallback. An ordinary test failure never falls back.
Mirage recognizes a full disk only on a Mac’s Linux jobs, where each job’s disk is its own and its size is the budget. Jobs on a Linux host’s Docker runtime, on Proxmox, in macOS VMs and on Cloudflare fail as usual; read the failed step’s log in GitHub.
Collect diagnostics
Record the GitHub run and job links, repository and account, attempt number, runner label, and Mirage’s route reason. For a host problem, include the machine’s Mirage version and the affected profile’s readiness message.
mirage host status
mirage host doctor
mirage host diagnostics --lines 80Share those details with the person administering Mirage for your team when asking for help. For a test failure, include the relevant GitHub step output. Review any diagnostics before sharing them, and leave out secrets, tokens, and pairing codes.