Delete One GitHub Release Asset Safely with gh
You will remove one named asset from a GitHub release using the installed GitHub CLI, then confirm that it is no longer listed. This is a destructive remote operation: deleting the asset does not remove the release or its tag, but the asset itself is not restored by the command.
The route
Jump straight to the step you need, or tick off Done means at the end.
Before you start
Allow about five minutes if you already know the repository, release tag and asset name. You need:
ghinstalled and authenticated for the target GitHub host.- permission to administer releases in the repository.
- the exact release tag, such as
v2.4.0. - the exact asset name, including its case and file extension.
This guide was checked with GitHub CLI 2.87.3, released on 23 February 2026. The local command is gh release delete-asset <tag> <asset-name>. Its command-specific options are --yes (or -y) and the inherited --repo (or -R).
Checkpoint: identify the target
First decide whether the command should use the repository in your current directory or an explicit repository. An explicit --repo is safer when you are working from a clone that could point at the wrong remote.
Set the values in this example to real values before running it. The shell variables do not change GitHub; they only make the later commands easier to inspect.
repo="OWNER/REPOSITORY"
tag="v2.4.0"
asset="example-linux-amd64.tar.gz"
printf 'repository: %s
release tag: %s
asset: %s
' "$repo" "$tag" "$asset"
Check the release and print its asset names:
gh release view "$tag" --repo "$repo" --json assets --jq '.assets[].name'
Expected output is one asset name per line. Do not continue until the line for $asset matches exactly. If the command reports that the release cannot be found, check the repository owner, repository name and tag. A tag is not interchangeable with a release title.
Checkpoint: confirm the repository context
If you chose to use the current directory instead of --repo, inspect the repository context before deleting anything:
gh repo view --json nameWithOwner --jq '.nameWithOwner'
It should print the repository you intended to change. The delete command accepts an optional host prefix in the repository value, so a GitHub Enterprise target can use a form such as ghe.example/OWNER/REPOSITORY when that host is configured in gh.
Delete the asset with a confirmation prompt
Run the command without --yes first. This keeps a final confirmation between your inspection and the destructive request.
gh release delete-asset "$tag" "$asset" --repo "$repo"
Review the prompt carefully. It should refer to the release identified by your tag and the asset name you checked. Confirm only if both are correct. A successful request normally returns without a lengthy result, so use the verification step below rather than treating a quiet terminal as proof.
No elevated Linux privileges are required. Do not prefix this with sudo: the operation is authorised by your GitHub credentials, not by local filesystem permissions.
Automate a deletion only after testing it
For a reviewed script or a non-interactive job, --yes skips the confirmation prompt:
gh release delete-asset "$tag" "$asset" --repo "$repo" --yes
Use this only when the tag, asset and repository came from trusted, validated values. Never pass an unchecked filename or user-supplied string into a destructive command. If a script may run against more than one repository, require an explicit allow-list or an explicit repository argument rather than relying on the current directory.
Verify that the asset is gone
Query the same release again:
gh release view "$tag" --repo "$repo" --json assets --jq '.assets[].name'
The deleted name should no longer appear. An empty response is valid when it was the release's only asset. To turn that check into a scriptable assertion, use:
if gh release view "$tag" --repo "$repo" --json assets --jq '.assets[].name' | grep -Fxq "$asset"; then
printf 'asset still present: %s
' "$asset" >&2
exit 1
fi
printf 'asset absent: %s
' "$asset"
The command can fail before producing a useful asset list if authentication, repository access or the network is unavailable. Treat that as an unverified state and investigate before repeating a deletion. Repeating the delete request for an already removed name is not a recovery method.
Recovery and safer alternatives
gh release delete-asset has no undo flag. If you removed the wrong asset, recover it by uploading a retained local copy with the release upload command:
gh release upload "$tag" ./example-linux-amd64.tar.gz --repo "$repo"
This creates a new release asset from the local file. It is not a byte-for-byte recovery if the original file is unavailable, and it may not preserve every property of the old asset. Confirm the replacement with gh release view before treating the incident as resolved.
If your actual aim is to replace an asset, check the upload command's current behaviour first. Its --clobber option can delete and re-upload an existing asset with the same name, which is a different operation from deleting an asset and leaving the release without it. If you only want to hide a release, deleting an individual asset is the wrong scope: it does not delete the release, tag or other assets.
Common traps
- Wrong repository: omitting
--repouses repository context inferred bygh. PrintnameWithOwnerwhen there is any doubt. - Wrong identifier: pass the release tag, not the release's display name. List the target release with
gh release viewbefore making changes. - Near-match asset name: asset names are not a glob here. Copy the exact value printed by the JSON query.
- Assuming a local file is relevant: the asset argument names the remote release asset. It is not a path to a local file.
- Skipping verification: a command that exits quietly still deserves a follow-up query, especially in automation.
Done means
- The repository and release tag were checked explicitly.
- The exact remote asset name was listed before deletion.
- The confirmation prompt was accepted, or
--yeswas used only in a reviewed automation path. - A second release query shows that the asset name is absent.
- A local replacement copy exists if recovery may be needed.