Filter GitHub Issues Reliably with gh issue list
You will finish with repeatable commands for listing open, closed or assigned GitHub issues, narrowing the results with labels and search qualifiers, and producing JSON for scripts. The examples match GitHub CLI 2.87.3, the installed version checked for this guide.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about ten minutes. You need the gh package, an authenticated GitHub CLI session with access to the repository you want to inspect, and a repository name such as OWNER/REPO. The commands only read issue data. Nothing here needs elevated privileges.
1. Check the installed command
Start by confirming which executable will run and which version provides its behaviour:
$ command -v gh
/home/linuxbrew/.linuxbrew/bin/gh
$ gh --version
gh version 2.87.3 (2026-02-23)
https://github.com/cli/cli/releases/tag/v2.87.3
Read the local command contract as well. This is useful when a machine has more than one GitHub CLI installation:
$ gh issue list --help
List issues in a GitHub repository. By default, this only lists open issues.
Checkpoint: if gh --version reports another release, recheck the help output before copying examples into automation. Flags and JSON fields can grow between releases.
2. Choose the repository explicitly
Inside a local checkout, gh issue list can infer the repository from its Git remote. That is convenient, but it is also an easy way to inspect the wrong project when you are in a similarly named directory. Use --repo when the target matters:
$ gh issue list --repo OWNER/REPO --limit 10
Issues for OWNER/REPO
#123 Example issue title (bug)
Replace both placeholders with a real owner and repository. The inherited option also accepts HOST/OWNER/REPO for a GitHub Enterprise host. The output is repository-specific, so a successful command against the wrong name can still look perfectly plausible.
With no state flag, the command lists open issues. The default maximum is 30 items, so an apparently short list may be truncated rather than complete.
3. Filter by state, label and ownership
Use --state to make the lifecycle you want visible in the command. It accepts open, closed or all:
$ gh issue list --repo OWNER/REPO --state all --limit 50
$ gh issue list --repo OWNER/REPO --state closed --assignee USERNAME
$ gh issue list --repo OWNER/REPO --label "help wanted" --label "bug"
Repeated --label options are useful when you need both labels. Quote a label containing spaces. Other direct filters include --author, --assignee, --mention, --milestone and --app. The special assignee value @me means the authenticated account:
$ gh issue list --repo OWNER/REPO --assignee "@me" --state open --limit 20
Checkpoint: verify the requested state and count rather than assuming the default. If you need every result, choose a limit deliberately and consider whether the repository may exceed it.
4. Use GitHub search syntax for the awkward cases
--search passes a GitHub issue search query, which lets you combine conditions that do not have a dedicated flag. For example, this finds unassigned issues and shows the oldest first:
$ gh issue list --repo OWNER/REPO \
--search "no:assignee sort:created-asc" \
--limit 25
Search qualifiers can cover labels, authors, dates, mentions, milestones and text in issue titles or bodies. Use quotes around the complete query so the shell passes it as one argument. A multi-word value inside the query needs its own quotes, for example label:"needs review".
Search results still obey the command's state default unless the query or --state changes it. If you are investigating old work, include --state all rather than silently missing closed issues.
5. Limit the columns for a script
Human-readable output is good for a quick scan. For a script, request named JSON fields instead of scraping the display table:
$ gh issue list --repo OWNER/REPO \
--state open \
--limit 100 \
--json number,title,labels,assignees,updatedAt,url
[
{
"number": 123,
"title": "Example issue title",
"labels": [],
"assignees": [],
"updatedAt": "2026-09-23T09:00:00Z",
"url": "https://github.com/OWNER/REPO/issues/123"
}
]
Field names are explicit in the command's help. Available fields in this installed release include number, title, body, state, stateReason, labels, milestone, assignees, author, createdAt, updatedAt and url. Ask for only what the consumer needs. Issue bodies and titles are untrusted text, so do not execute or evaluate them as shell input.
Use --jq to select or transform the resulting JSON with jq syntax. Use --template for a Go template when you need a controlled text report:
$ gh issue list --repo OWNER/REPO --limit 20 \
--json number,title \
--jq '.[] | "#\(.number) \(.title)"'
#123 Example issue title
Do not combine a hand-written parser for the default table with changing terminal spacing. JSON is the stable boundary.
6. Troubleshoot an empty or surprising list
An empty result is not automatically an error. Check these in order:
- Print the target with
--repoand confirm the owner and repository spelling. - Run the same command with
--state allto rule out the open-only default. - Remove
--search, labels and assignment filters one at a time. - Increase
--limitif the visible count is exactly the limit you chose. - Check authentication and repository visibility with
gh auth status.
For browser-based review, --web opens the issue list in a browser instead of printing it. Treat that as an interactive diversion: it is not suitable for a scheduled check or a pipeline that expects stdout.
There is no undo step because gh issue list does not modify issues, labels, milestones or repository settings. The main safety boundary is data handling: avoid putting private issue bodies into logs, tickets or generated reports unless the recipients are authorised to see them.
Done means
- You confirmed the installed GitHub CLI version and help output.
- You can select the repository explicitly with
--repo. - You know that the default is open issues with a 30-item limit.
- You can combine state, labels, assignees and GitHub search qualifiers.
- You use
--json,--jqor--templateinstead of parsing the table. - You can explain an empty result without changing remote data.