Watch a GitHub Actions Run and Fail Scripts Reliably
You will finish with a repeatable way to find a GitHub Actions run, watch it until completion, and make a shell command return failure when the run fails. Allow about ten minutes. You need GitHub CLI, access to the repository, and a shell. The examples use gh 2.87.3, the version resolved by gh on the machine used for this guide.
The route
Jump straight to the step you need, or tick off Done means at the end.
Checkpoint
This guide watches an existing run. It does not start, cancel, rerun or delete one, and it does not change repository settings. No elevated privileges are needed.
1. Check the installed command
Confirm which binary your shell will run and read its local help:
$ command -v gh
/home/linuxbrew/.linuxbrew/bin/gh
$ gh version
gh version 2.87.3 (2026-02-23)
$ gh run watch --help
The command's synopsis is gh run watch <run-id>. The installed help also documents --compact, which only shows relevant or failed steps. The local compressed manpage lists the core options but does not list that newer display option, so use the command's own help when versions differ.
A version check is useful here because GitHub CLI is often installed by more than one package manager. If command -v gh points somewhere unexpected, fix your PATH or invoke the intended absolute path before continuing.
2. Find the run ID
Run the list command from a checked-out repository with a GitHub remote, or name the repository explicitly. Limit the result while you are choosing a run:
$ gh run list --repo OWNER/REPOSITORY --limit 5
STATUS TITLE WORKFLOW BRANCH EVENT ID AGE
... ... ... ... ... 1234567 ...
Replace OWNER/REPOSITORY with a real value. Copy the numeric value in the ID column, not the workflow run number or a pull request number. To narrow the list, add a documented filter such as --branch main or --status in_progress:
$ gh run list --repo OWNER/REPOSITORY --branch main --limit 3
If the command says it cannot determine the base repository, the current directory has no usable GitHub remote. Keep the explicit --repo form, or change to a checkout whose remote identifies the intended repository. If it reports an authentication or permission error, resolve that with gh auth status; do not paste a token into the command line.
3. Watch the run
Pass the copied run ID and repository to gh run watch:
$ gh run watch 1234567 --repo OWNER/REPOSITORY
Refreshing checks status every 3 seconds. Press Ctrl-C to quit.
... workflow and step progress ...
The default refresh interval is three seconds. The command keeps refreshing until the run completes, then returns to the shell. Output is live status, so names, timings and step results will differ from this example. The command does not change the run.
For a busy workflow, reduce the noise without changing what GitHub executes:
$ gh run watch 1234567 --repo OWNER/REPOSITORY --compact
Use --interval 10 when a slower refresh is preferable:
$ gh run watch 1234567 --repo OWNER/REPOSITORY --interval 10
The interval is in seconds and must be an integer. A very short interval creates needless API traffic and does not make a remote runner finish sooner.
4. Make the shell notice a failed run
Without an explicit status option, watching and judging are easy to confuse. Add --exit-status when a script or follow-up command must stop if the workflow conclusion is not successful:
$ gh run watch 1234567 --repo OWNER/REPOSITORY --exit-status
$ printf 'watch exit status: %s\n' "$?"
watch exit status: 0
On a successful run, the final status is zero. On a failed run, --exit-status makes the command return non-zero. A non-zero result can also mean that the command could not fetch or watch the run, so read the diagnostic output before calling it a workflow failure.
This is the useful shell pattern:
$ gh run watch 1234567 --repo OWNER/REPOSITORY --exit-status && notify-send 'GitHub Actions run passed'
The notification is sent only after a zero exit status. To handle both outcomes explicitly:
$ if gh run watch 1234567 --repo OWNER/REPOSITORY --exit-status; then
> echo 'run passed'
> else
> echo 'run failed or could not be watched' >&2
> exit 1
> fi
5. Diagnose the common traps
The wrong repository: an ID only makes sense in its repository context. Always retain --repo when a script may run outside the checkout, or when several repositories are involved.
The wrong number: a workflow run has a numeric database ID and may also have a displayed run number. Take the value shown as ID by gh run list. If you are unsure, list the run again with JSON fields and inspect the result:
$ gh run list --repo OWNER/REPOSITORY --limit 1 --json databaseId,status,conclusion
[{"databaseId":1234567,"status":"completed","conclusion":"success"}]
Stopping early: pressing Ctrl-C stops your local watch. It does not cancel the GitHub Actions run. You can watch the same ID again later. Do not use a cancellation command as a substitute for interrupting a display.
Authentication: the watch command needs permission to read the checks associated with the run. Fine-grained personal access tokens are not supported by this command in the installed help because the required checks:read permission cannot currently be created for that token type. Use the supported GitHub CLI authentication arrangement for the account and repository, and keep credentials out of scripts and process arguments.
Done means
- You verified the
ghbinary and version used by the shell. - You found the run ID with
gh run listand confirmed its repository. - You watched the run with an explicit interval or the three-second default.
- You used
--exit-statuswhen a script needed to detect failure. - You know Ctrl-C stops watching but does not cancel the remote run.
- You made no repository, workflow or service changes.