Skip to content

Say why the install failed - #33

Merged
zmaril merged 1 commit into
mainfrom
install-failure-clarity
Aug 25, 2026
Merged

Say why the install failed#33
zmaril merged 1 commit into
mainfrom
install-failure-clarity

Conversation

@zmaril

@zmaril zmaril commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

This came out of using the action in cratebank, where it failed twice and took three attempts to work out why. The cause was rate limiting; the message said the repository had no releases.

The bug

latest_version() {
    fetch "${API}/releases/latest" |
        sed -n 's/.*"tag_name".*/\1/p' |
        head -n 1
}

A pipeline's exit status is its last command's, so fetch failing is invisible behind head. A refused request parses to an empty string, latest_version "succeeds" with no output, and control falls to the next check:

straitjacket: no published release found. Set STRAITJACKET_VERSION to install a specific tag

Which reads as "this repository has no releases." The repository had releases. The request was rate limited.

The response is now captured before it is parsed, so the failure propagates and the accurate message fires. Verified both ways: a real request still resolves v0.1.1, and a request that errors now returns non-zero instead of an empty string.

The message

The accurate message now says what is usually true:

GitHub allows 60 unauthenticated API requests an hour per address, and CI runners share addresses, so this is usually rate limiting rather than a missing release. Set GITHUB_TOKEN — any valid token raises the limit, and it need not have access to this repository while it is public. Or pin STRAITJACKET_VERSION, which skips the API entirely.

The token input said it was "Needed only while the Straitjacket repository is private; a public release needs none." That is true of access and false of rate limits, and it is why I ruled out the token as a cause on the first pass. It now says both.

Retries

The first of the two failures was curl: (35) Recv failure: Connection reset by peer. A dropped connection on the way to a release is not a reason to fail a build, so fetches and downloads now pass --retry 3 --retry-delay 2. Rate limiting is not retried away — an hour is not a retry window — which is what the message is for.

Telling the two failures apart

In a checks list, an install that could not happen and a scan that found something are the same thing: a red job whose log ends in exit code 1. The only tell was that the Run straitjacket step showed skipped, which you have to open the job to see.

The install step now annotates:

Straitjacket could not be installed — The scan did not run. This is an installation failure, not a finding — see the log above, and set the action's token input if it mentions rate limiting.

The action already distinguishes 2 operational failure from 1 findings in its exit-code output; this makes the same distinction visible at the point where someone is deciding whether to care.

Checked

sh -n and shellcheck clean. The retry flags are unquoted on purpose with a shellcheck disable=SC2086, since they are an argument list.

🤖 Generated with Claude Code

https://claude.ai/code/session_01361quprG7tUgaVFKAmKtJZ

`install.sh` piped the release fetch straight into `sed`, so the fetch's
exit status was hidden behind `head`. A refused request parsed to an
empty string and took the "no published release found" path, which reads
as "this repository has no releases" and sends whoever hit it looking in
the wrong place. It cost three attempts to diagnose in a downstream
repository today, and the cause was rate limiting.

The response is now read before it is parsed, so the accurate message
fires; that message says what is usually true, which is that GitHub
allows 60 unauthenticated API requests an hour per address and CI
runners share addresses.

Downloads retry three times, because a reset connection on the way to a
release is not a reason to fail a build -- the same job failed that way
once before it failed with the 403.

The action annotates an installation failure as one. In a checks list
"could not install" and "found something" are both a red job ending in
`exit code 1`, and the difference matters to whoever opens it. The
`token` input said it was needed only for a private repository, which is
true of access and false of rate limits; it now says both.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01361quprG7tUgaVFKAmKtJZ
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
straitjacket 61f0b2b Commit Preview URL

Branch Preview URL
Aug 24 2026, 10:32 PM

@zmaril
zmaril merged commit 30c0ee0 into main Aug 25, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant