{"record":{"id":"d8b122af940a0200","repo":"remix-run/react-router","slug":"github-token-environment-variable-is-required","errorCode":null,"errorMessage":"GITHUB_TOKEN environment variable is required","messagePattern":"GITHUB_TOKEN environment variable is required","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"scripts/utils/github.ts","lineNumber":11,"sourceCode":"import { request } from \"@octokit/request\";\n\nimport { getGitTag } from \"./packages.ts\";\n\nconst OWNER = \"remix-run\";\nconst REPO = \"react-router\";\n\nfunction getToken(): string {\n  let token = process.env.GITHUB_TOKEN;\n  if (!token) {\n    throw new Error(\"GITHUB_TOKEN environment variable is required\");\n  }\n  return token;\n}\n\nfunction requestOptions() {\n  return {\n    owner: OWNER,\n    repo: REPO,\n    headers: { authorization: `token ${getToken()}` },\n  };\n}\n\nexport type CreateReleaseResult =\n  | { status: \"created\"; url: string }\n  | { status: \"skipped\"; reason: string }\n  | { status: \"error\"; error: string };\n\n/**","sourceCodeStart":1,"sourceCodeEnd":29,"githubUrl":"https://github.com/remix-run/react-router/blob/1fd704a7dabcbe3ae09d7387b460e6acaba30ec1/scripts/utils/github.ts#L1-L29","documentation":"This is a custom guard error thrown by getToken() in scripts/utils/github.ts before any GitHub API request is dispatched. The scripts/utils/github.ts module wraps @octokit/request to talk to the remix-run/react-router repo, and every requestOptions() call embeds an authorization header built from getToken(). Without a token, the Octokit request would either fail with a 401 at the API boundary or hit rate limits, so the script fails fast with this explicit message instead.","triggerScenarios":"Running any release/tag/PR script that imports scripts/utils/github.ts (which calls getGitTag from packages.ts and then requestOptions()) without GITHUB_TOKEN exported in the environment. Concretely: invoking scripts that call getToken() -> requestOptions() -> @octokit/request for repo data, releases, or tags while process.env.GITHUB_TOKEN is undefined or empty.","commonSituations":"Running release scripts locally without sourcing the .env that defines GITHUB_TOKEN; CI (GitHub Actions) where the secret was not mapped to the GITHUB_TOKEN env var on the job; token set in a different shell session than the one running the script; typo in the variable name (e.g. GH_TOKEN vs GITHUB_TOKEN); the script being invoked through a subshell or tman run that does not inherit the parent environment.","solutions":["Export a valid GitHub personal access token before running the script: GITHUB_TOKEN=ghp_xxx <script> (or `export GITHUB_TOKEN=ghp_xxx` then run).","If using a devkey registry per repo conventions, add GITHUB_TOKEN to dev-setup/config/devkey/registry.kdl and run the script via `devkey run github-token -- <cmd>` so the grant is injected.","In CI, ensure the job has `env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}` (or a PAT secret) on the step or job.","Verify the token is visible to the process with a one-off check that reads process.env.GITHUB_TOKEN by NAME (never print the value) — e.g. assert it is non-empty before invoking the script."],"exampleFix":"// before\n$ pnpm tsx scripts/some-release-script.ts\nError: GITHUB_TOKEN environment variable is required\n\n// after (one-off)\n$ export GITHUB_TOKEN=$(devkey need github-token)\n$ pnpm tsx scripts/some-release-script.ts\n\n// after (CI step)\n- name: Run release script\n  env:\n    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n  run: pnpm tsx scripts/some-release-script.ts","handlingStrategy":"validation","validationCode":"// Run before invoking any script that imports scripts/utils/github.ts\nfunction assertGithubToken(): void {\n  if (!process.env.GITHUB_TOKEN) {\n    throw new Error(\n      \"GITHUB_TOKEN is missing. Set it via `export GITHUB_TOKEN=$(devkey need github-token)` \" +\n        \"or map `secrets.GITHUB_TOKEN` to env in CI.\"\n    );\n  }\n}\nassertGithubToken();","typeGuard":"// Environment guards narrow on presence, not type (always string|undefined).\nconst hasGithubToken = (): boolean =>\n  typeof process.env.GITHUB_TOKEN === \"string\" && process.env.GITHUB_TOKEN.length > 0;","tryCatchPattern":"// Catch only to emit a friendlier message, then re-throw — never swallow.\ntry {\n  await runReleaseScript();\n} catch (err) {\n  if (err instanceof Error && err.message.includes(\"GITHUB_TOKEN\")) {\n    console.error(\n      \"Missing GITHUB_TOKEN. Create a PAT at https://github.com/settings/tokens \" +\n        \"with `repo` scope and export it before re-running.\"\n    );\n    process.exit(1);\n  }\n  throw err; // unrelated error — preserve stack\n}","preventionTips":["Add a preflight env check at the top of every release/tag script that imports github.ts so the failure is reported before any network work begins.","Document the required env var in the script's JSDoc and in AGENTS.md so contributors know to set it before the first run.","In CI, fail the job fast with a dedicated `Check env` step rather than letting the script fail mid-request.","Use the repo's devkey registry for local runs so the token is never copy-pasted into shell history.","Never log the token value — verify presence by length/non-emptiness, not by printing."],"tags":["github-api","environment-variables","authentication","release-scripts","config"],"backgroundTag":null,"analyzedSha":"1fd704a7dabcbe3ae09d7387b460e6acaba30ec1","analyzedAt":"2026-08-12T13:54:57.804Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}