paperclipai/paperclip · error · Error
GitHub Actions read failed
Error message
GitHub Actions read failed: ${cause.message} What it means
createActionsReader wraps every fetch to api.github.com. When the fetch itself rejects (network error, DNS failure, 30s AbortSignal timeout, TLS error) and the attempt is not retryable — retry budget exhausted (attempt >= attempts, default 4) or the caller's deadline has passed — it throws this error carrying the underlying cause message. Retrying attempts log instead of throwing.
Solutions
- Check general connectivity: curl -sS https://api.github.com/rate_limit with your token.
- Check the GitHub status page for an ongoing Actions/API incident and retry after it clears.
- If behind a proxy, configure HTTPS_PROXY/node proxy support so fetch can reach api.github.com.
- Rerun the release poll — it is a transport problem, not a verdict on the source; a later run usually succeeds.
- If it fires near the poll timeout, that is by design (the reader stops retrying past the deadline); rerun to get a fresh 45-minute window.
Example fix
// before: no proxy configured
const api = createActionsReader({ token });
// after: run with proxy env
// HTTPS_PROXY=http://proxy.corp:8080 node scripts/cloud-source-verification.mjs <sha>
const api = createActionsReader({ token, attempts: 6 }); Defensive patterns
Strategy: retry
Validate before calling
// preflight connectivity + auth before the long poll
const res = await fetch('https://api.github.com/rate_limit', { headers: { Authorization: `Bearer ${process.env.GITHUB_TOKEN}` } });
if (!res.ok) throw new Error(`GitHub unreachable or token invalid (HTTP ${res.status})`); Try / catch
try {
const proof = await waitForSourceVerification(sha, { api, timeoutMs });
} catch (e) {
if (e.message.startsWith('GitHub Actions read failed:')) {
console.error(`Transport error: ${e.message}. Check network/proxy and rerun the poll.`);
}
} Prevention
- Run the script on a machine with reliable access to api.github.com (no flaky VPN/proxy).
- Check the GitHub status page before long release polls.
- Raise the attempts option if your environment has frequent transient network errors.
- Remember retries are bounded by the poll deadline; timeouts near the end are expected, not bugs.
When it happens
Trigger: Network outage, offline machine, DNS failure, request exceeding AbortSignal.timeout(30_000), or the 4-attempt retry budget/45-minute polling deadline exhausted during repeated transport failures.
Common situations: Corporate proxy/firewall blocking api.github.com; GitHub Actions outage; running the script without network access; long CI poll where repeated transient errors exhaust retries near the deadline.
Understand the failure class
Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.
Related errors
- github_webhook_recovery_transport
- CreateOS process output connection failed.
- CreateOS process stream ended without an exit status.
- github_attachment_download_failed
- Migrator producer lookup failed: HTTP
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18).
Data as JSON: /api/errors/3bb2f75dc4eb6062.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/cloud-source-verification.mjs:112
token, fetchImpl = fetch, sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
attempts = 4, backoffMs = 1_000, log = console.log,
now = Date.now, deadlineAt = () => Infinity, maxRetryAfterMs = 60_000,
} = {}) {
return async (path) => {
for (let attempt = 1; ; attempt += 1) {
// Retry only while both the attempt budget and the caller's deadline
// leave room for the wait this attempt would cost.
const waitMs = backoffMs * attempt;
const retryable = attempt < attempts && now() + waitMs < deadlineAt();
const pause = (response) => sleep(retryAfterMs(response, maxRetryAfterMs) ?? waitMs);
let response;
try {
response = await fetchImpl(`https://api.github.com${path}`, {
headers: { Authorization: `Bearer ${token}`, Accept: "application/vnd.github+json", "X-GitHub-Api-Version": "2022-11-28" },
signal: AbortSignal.timeout(30_000), redirect: "error",
});
} catch (cause) {
if (!retryable) throw new Error(`GitHub Actions read failed: ${cause.message}`);
log(`GitHub Actions read failed (${cause.message}); retrying (${attempt}/${attempts - 1}).`);
await pause();
continue;
}
if (response.ok) {
try {
// A 200 whose body is truncated or undecodable is a transport
// failure like any other, so it belongs inside the retry.
return await response.json();
} catch (cause) {
if (!retryable) throw new Error(`GitHub Actions read failed: ${cause.message}`);
log(`GitHub Actions read body failed (${cause.message}); retrying (${attempt}/${attempts - 1}).`);
await pause();
continue;
}
}
if (!retryable || !(TRANSIENT_READ_STATUSES.has(response.status) || rateLimited(response))) {
throw new Error(`GitHub Actions read failed (HTTP ${response.status}).`);View on GitHub (pinned to 3f1d897a7c)