paperclipai/paperclip · error · Error

GITHUB_TOKEN with Actions read access is required.

Error message

GITHUB_TOKEN with Actions read access is required.

What it means

The CLI entry point of cloud-source-verification.mjs requires GITHUB_TOKEN in the environment because every GitHub Actions read needs a bearer token with Actions read access. When the env var is unset or empty, the script throws this immediately before any API call, and exits with code 1.

Solutions

  1. Export the token before running: export GITHUB_TOKEN=$(gh auth token) or paste a PAT with Actions read.
  2. In GitHub Actions, pass the secret: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} (or a PAT secret) on the step.
  3. Check the variable name is exactly GITHUB_TOKEN, not GH_TOKEN or GITHUB_PAT: echo "${GITHUB_TOKEN:+set}".
  4. Verify the token can read Actions: curl -sS -H "Authorization: Bearer $GITHUB_TOKEN" https://api.github.com/repos/paperclipai/paperclip/actions/workflows/cloud-readiness.yml

Example fix

// before
$ node scripts/cloud-source-verification.mjs abc123...
GITHUB_TOKEN with Actions read access is required.
// after
$ export GITHUB_TOKEN=$(gh auth token)
$ node scripts/cloud-source-verification.mjs abc123...
Defensive patterns

Strategy: validation

Validate before calling

if (!process.env.GITHUB_TOKEN) {
  throw new Error('GITHUB_TOKEN must be set (PAT or GITHUB_TOKEN secret with Actions read)');
}

Type guard

function hasGithubToken(env = process.env) {
  return typeof env.GITHUB_TOKEN === 'string' && env.GITHUB_TOKEN.length > 0;
}

Try / catch

try {
  await runVerification(sha);
} catch (e) {
  if (e.message.includes('GITHUB_TOKEN')) {
    console.error('Set GITHUB_TOKEN, e.g. export GITHUB_TOKEN=$(gh auth token)');
    process.exitCode = 1;
  }
}

Prevention

When it happens

Trigger: Running `node scripts/cloud-source-verification.mjs <sha>` without GITHUB_TOKEN exported — fresh shell, CI job missing the env mapping, or a .env file that was never loaded (the script reads process.env only).

Common situations: Local run before sourcing credentials; GitHub Actions step that forgot `env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}`; token exported under a different name (GH_TOKEN, GITHUB_PAT); dotenv not loaded in a plain node script.

Understand the failure class

Background: "environment variable is not set" and "Missing keys in environment" errors: what missing required env var messages mean and how to fix them — this error's family across 28 libraries.

Related errors


AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18). Data as JSON: /api/errors/077eb161580514b2. Report an issue: GitHub.

Appendix: source

Thrown at scripts/cloud-source-verification.mjs:156

export async function waitForSourceVerification(sha, {
  api, now = Date.now, sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
  timeoutMs = 45 * 60_000, intervalMs = 30_000, log = console.log,
} = {}) {
  assertSha(sha);
  const deadline = now() + timeoutMs;
  log(`Waiting for ${sourceVerificationJob} for ${sha}.`);
  while (now() < deadline) {
    const result = await readSourceVerification(sha, api);
    if (result) return result;
    const remaining = deadline - now();
    if (remaining > 0) await sleep(Math.min(intervalMs, remaining));
  }
  throw new Error(`Cloud source verification timed out for ${sha}. Rerun Cloud readiness before retrying the release.`);
}

if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
  try {
    if (!process.env.GITHUB_TOKEN) throw new Error("GITHUB_TOKEN with Actions read access is required.");
    // One deadline for both layers: the reader stops retrying when the poll it
    // serves is out of time, instead of extending the wait past its timeout.
    const timeoutMs = 45 * 60_000;
    const deadline = Date.now() + timeoutMs;
    const api = createActionsReader({ token: process.env.GITHUB_TOKEN, deadlineAt: () => deadline });
    const proof = await waitForSourceVerification(process.argv[2], { api, timeoutMs });
    const message = `Source verification passed for ${proof.sha}: https://github.com/${repository}/actions/runs/${proof.runId}/attempts/${proof.attempt} (job ${proof.jobId}).`;
    console.log(message);
    if (process.env.GITHUB_STEP_SUMMARY) await appendFile(process.env.GITHUB_STEP_SUMMARY, `${message}\n`);
  } catch (error) {
    console.error(error.message);
    process.exitCode = 1;
  }
}

View on GitHub (pinned to 3f1d897a7c)