{"record":{"id":"3bb2f75dc4eb6062","repo":"paperclipai/paperclip","slug":"github-actions-read-failed-cause-message","errorCode":null,"errorMessage":"GitHub Actions read failed: ${cause.message}","messagePattern":"GitHub Actions read failed: (.+?)","errorType":"console","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/cloud-source-verification.mjs","lineNumber":112,"sourceCode":"  token, fetchImpl = fetch, sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)),\n  attempts = 4, backoffMs = 1_000, log = console.log,\n  now = Date.now, deadlineAt = () => Infinity, maxRetryAfterMs = 60_000,\n} = {}) {\n  return async (path) => {\n    for (let attempt = 1; ; attempt += 1) {\n      // Retry only while both the attempt budget and the caller's deadline\n      // leave room for the wait this attempt would cost.\n      const waitMs = backoffMs * attempt;\n      const retryable = attempt < attempts && now() + waitMs < deadlineAt();\n      const pause = (response) => sleep(retryAfterMs(response, maxRetryAfterMs) ?? waitMs);\n      let response;\n      try {\n        response = await fetchImpl(`https://api.github.com${path}`, {\n          headers: { Authorization: `Bearer ${token}`, Accept: \"application/vnd.github+json\", \"X-GitHub-Api-Version\": \"2022-11-28\" },\n          signal: AbortSignal.timeout(30_000), redirect: \"error\",\n        });\n      } catch (cause) {\n        if (!retryable) throw new Error(`GitHub Actions read failed: ${cause.message}`);\n        log(`GitHub Actions read failed (${cause.message}); retrying (${attempt}/${attempts - 1}).`);\n        await pause();\n        continue;\n      }\n      if (response.ok) {\n        try {\n          // A 200 whose body is truncated or undecodable is a transport\n          // failure like any other, so it belongs inside the retry.\n          return await response.json();\n        } catch (cause) {\n          if (!retryable) throw new Error(`GitHub Actions read failed: ${cause.message}`);\n          log(`GitHub Actions read body failed (${cause.message}); retrying (${attempt}/${attempts - 1}).`);\n          await pause();\n          continue;\n        }\n      }\n      if (!retryable || !(TRANSIENT_READ_STATUSES.has(response.status) || rateLimited(response))) {\n        throw new Error(`GitHub Actions read failed (HTTP ${response.status}).`);","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/scripts/cloud-source-verification.mjs#L94-L130","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: no proxy configured\nconst api = createActionsReader({ token });\n// after: run with proxy env\n// HTTPS_PROXY=http://proxy.corp:8080 node scripts/cloud-source-verification.mjs <sha>\nconst api = createActionsReader({ token, attempts: 6 });","handlingStrategy":"retry","validationCode":"// preflight connectivity + auth before the long poll\nconst res = await fetch('https://api.github.com/rate_limit', { headers: { Authorization: `Bearer ${process.env.GITHUB_TOKEN}` } });\nif (!res.ok) throw new Error(`GitHub unreachable or token invalid (HTTP ${res.status})`);","typeGuard":null,"tryCatchPattern":"try {\n  const proof = await waitForSourceVerification(sha, { api, timeoutMs });\n} catch (e) {\n  if (e.message.startsWith('GitHub Actions read failed:')) {\n    console.error(`Transport error: ${e.message}. Check network/proxy and rerun the poll.`);\n  }\n}","preventionTips":["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."],"tags":["network","github-api","timeout","retry-exhausted"],"backgroundTag":"http-request-failed","analyzedSha":"3f1d897a7c018d76563a21c6e39c3c9b03933622","analyzedAt":"2026-09-18T08:03:59.046Z","contentChangedAt":"2026-09-18T08:03:59.046Z","schemaVersion":2},"datasetVersion":"2026-09-22T01:17:13.364Z"}