Read-only Python and Node.js scripts that find GitHub API problems — pagination that stops at the first page, 404s masking 403s, webhooks failing unnoticed and Apps missing a permission. They report and print the repair; they never write.
Every script here is read only. They hold a credential to a live account, so none of them writes: each one reads through the API, reports exactly what is wrong, and prints the repair for you to run.
By Allan Niñal — AI Solutions Engineer. I build AI powered tools, data products, and AWS automation. Full write ups with diagrams for each fix live at allanninal.dev/github.
- a permission error is disguised as 404 Not Found — https://www.allanninal.dev/github/404-masking-403/
- GITHUB_TOKEN gets 1,000 an hour, shared across the repo — https://www.allanninal.dev/github/actions-token-repo-scoped-limit/
- a hardcoded installation id stops matching reality — https://www.allanninal.dev/github/app-installation-id-hardcoded/
- The App installation was requested and never approved — https://www.allanninal.dev/github/app-installation-request-pending/
- a 404 that means the App is not installed on that repo — https://www.allanninal.dev/github/app-not-installed-on-repo/
- the App was never subscribed to the event it waits for — https://www.allanninal.dev/github/app-not-subscribed-to-event/
- resource not accessible by integration on one endpoint — https://www.allanninal.dev/github/app-permission-missing/
- a new App permission that installers never accepted — https://www.allanninal.dev/github/app-permission-upgrade-not-accepted/
- the App's rate limit never grew with the installation — https://www.allanninal.dev/github/app-rate-limit-not-scaling/
- the installation token was narrowed below what the job needs — https://www.allanninal.dev/github/app-token-scoped-down-too-far/
- the GitHub App has no webhook URL configured — https://www.allanninal.dev/github/app-webhook-url-unset/
- 401 Bad credentials on every endpoint, even public ones — https://www.allanninal.dev/github/bad-credentials-401/
- the client still sends a username and password to the API — https://www.allanninal.dev/github/basic-auth-password-removed/
- branch protection is unreadable without admin, not absent — https://www.allanninal.dev/github/branch-protection-requires-admin/
- a classic PAT passed its expiry and everything broke at once — https://www.allanninal.dev/github/classic-pat-expired/
- code search is billed to its own 10 a minute bucket — https://www.allanninal.dev/github/code-search-bucket-exhausted/
- The scope is right, the account's role on the repo is read — https://www.allanninal.dev/github/collaborator-permission-insufficient/
- the compare endpoint stops at 250 commits and says nothing — https://www.allanninal.dev/github/compare-250-commit-cap/
- the deploy key is read-only and the push needs write — https://www.allanninal.dev/github/deploy-key-read-only-assumed-write/
- the same webhook URL is registered on the org and the repo — https://www.allanninal.dev/github/duplicate-webhooks/
- the endpoint ignores page and returns page one forever — https://www.allanninal.dev/github/endpoint-ignores-page-param/
- The client is pointed at the wrong GitHub host entirely — https://www.allanninal.dev/github/enterprise-endpoint-on-dotcom/
- rotating the token invalidates every cached ETag at once — https://www.allanninal.dev/github/etag-invalidated-by-token-rotation/
- A 403 that means the feature is disabled, not the permission — https://www.allanninal.dev/github/feature-disabled-endpoint-403/
- A fine-grained token that is waiting for an org owner — https://www.allanninal.dev/github/fine-grained-pat-pending-approval/
- Every call succeeds and reports on a fork, not the upstream — https://www.allanninal.dev/github/fork-vs-upstream-confusion/
- GraphQL returns 200 with an errors array and null data — https://www.allanninal.dev/github/graphql-200-with-errors/
- Nobody measured what the GraphQL query actually costs — https://www.allanninal.dev/github/graphql-cost-not-measured/
- A GraphQL connection asks for first: 500 and is rejected — https://www.allanninal.dev/github/graphql-first-over-100/
- GraphQL node ids get stored where REST ids are expected — https://www.allanninal.dev/github/graphql-id-vs-databaseid/
- A mutation costs five secondary points, a query costs one — https://www.allanninal.dev/github/graphql-mutation-secondary-cost/
- Nested GraphQL connections truncate at 100 per parent — https://www.allanninal.dev/github/graphql-nested-pagination-ignored/
- A nested GraphQL query requests more than 500,000 nodes — https://www.allanninal.dev/github/graphql-node-limit-exceeded/
- GraphQL data is present but individual fields are null — https://www.allanninal.dev/github/graphql-partial-data-nulls/
- GraphQL points run out in a bucket separate from REST — https://www.allanninal.dev/github/graphql-rate-limited/
- GraphQL search stops at the same 1,000 results as REST — https://www.allanninal.dev/github/graphql-search-same-1000-cap/
- A GraphQL query times out at 10s and is charged anyway — https://www.allanninal.dev/github/graphql-timeout-point-penalty/
- the installation covers only some repositories, silently — https://www.allanninal.dev/github/installation-repository-selection-partial/
- the installation is suspended and every call it makes 403s — https://www.allanninal.dev/github/installation-suspended/
- the installation token expired an hour into the job — https://www.allanninal.dev/github/installation-token-expired/
- some endpoints refuse an installation token whatever it holds — https://www.allanninal.dev/github/installation-token-rejected-by-endpoint/
- The org's IP allow list refuses the runner, not the token — https://www.allanninal.dev/github/ip-allow-list-blocks-requests/
- clock drift puts the JWT iat claim in GitHub's future — https://www.allanninal.dev/github/jwt-clock-drift-iat/
- a GitHub App JWT that expires in an hour is refused — https://www.allanninal.dev/github/jwt-exp-too-far-future/
- the App JWT is signed with the wrong key or algorithm — https://www.allanninal.dev/github/jwt-wrong-key-or-algorithm/
- only the first page is read because the Link header is ignored — https://www.allanninal.dev/github/link-header-not-followed/
- GitHub answers 404 for the wrong verb, never 405 — https://www.allanninal.dev/github/missing-endpoint-404-vs-405/
- the endpoint accepts a scope your token was never given — https://www.allanninal.dev/github/missing-oauth-scope/
- polling without ETags spends full quota on unchanged data — https://www.allanninal.dev/github/no-conditional-requests/
- The org restricts OAuth Apps and this one was never approved — https://www.allanninal.dev/github/oauth-app-access-restricted/
- one user revoked your app and only their token is dead — https://www.allanninal.dev/github/oauth-token-revoked-by-user/
- Enforcing 2FA removed the machine account from the org — https://www.allanninal.dev/github/org-2fa-requirement-removed-member/
- The org's base permission dropped and repos vanished — https://www.allanninal.dev/github/org-base-permission-changed/
- The org caps token lifetime below your rotation interval — https://www.allanninal.dev/github/org-token-lifetime-policy/
- An outside collaborator has repos in an org, not the org — https://www.allanninal.dev/github/outside-collaborator-invisible-org-data/
- a read-only job holds a token that can delete repositories — https://www.allanninal.dev/github/over-scoped-token/
- per_page is unset so every list costs 3.3x more requests — https://www.allanninal.dev/github/per-page-default-30/
- per_page above 100 is clamped and never rejected — https://www.allanninal.dev/github/per-page-over-100-clamped/
- the x-poll-interval header is ignored on events endpoints — https://www.allanninal.dev/github/poll-interval-header-ignored/
- the integration polls for events a webhook would push — https://www.allanninal.dev/github/polling-instead-of-webhooks/
- a pull request's files and commits lists are both capped — https://www.allanninal.dev/github/pr-files-and-commits-caps/
- A repository went private and anonymous callers now see 404 — https://www.allanninal.dev/github/private-repo-visibility-changed/
- core REST quota is exhausted and every call returns 403 — https://www.allanninal.dev/github/rate-limit-core-exhausted/
- requests go out anonymous and are capped at 60 an hour — https://www.allanninal.dev/github/rate-limit-unauthenticated/
- the Link header has no rel=last so the page count breaks — https://www.allanninal.dev/github/rel-last-absent/
- the repository is archived so every write returns 403 — https://www.allanninal.dev/github/repo-archived-writes-403/
- the repository is disabled and behaves like a ghost — https://www.allanninal.dev/github/repo-disabled/
- the repository was renamed and every call now 301s — https://www.allanninal.dev/github/repo-renamed-301-redirect/
- expensive requests are killed at ten seconds with a 502 — https://www.allanninal.dev/github/request-timeout-502/
- 403 Resource not accessible by personal access token — https://www.allanninal.dev/github/resource-not-accessible-by-pat/
- the client ignores retry-after and keeps hammering the API — https://www.allanninal.dev/github/retry-after-ignored/
- org lists silently omit SSO-enforced organizations — https://www.allanninal.dev/github/saml-partial-results/
- The SAML session lapsed and the authorization went with it — https://www.allanninal.dev/github/saml-session-expired/
- The token is valid and was never SSO-authorized for the org — https://www.allanninal.dev/github/saml-token-not-authorized/
- search returns at most 1,000 results whatever total_count says — https://www.allanninal.dev/github/search-1000-result-cap/
- search has its own 30-per-minute bucket and drains separately — https://www.allanninal.dev/github/search-bucket-exhausted/
- incomplete_results is true and the search answer is partial — https://www.allanninal.dev/github/search-incomplete-results/
- over 100 concurrent requests trips a secondary rate limit — https://www.allanninal.dev/github/secondary-limit-concurrency/
- bulk issue or comment creation exceeds 80 requests a minute — https://www.allanninal.dev/github/secondary-limit-content-creation/
- a hot endpoint burns 900 points a minute and gets throttled — https://www.allanninal.dev/github/secondary-limit-points-per-minute/
- the token expires in days and nothing is watching the clock — https://www.allanninal.dev/github/token-expiring-soon/
- the token is passed as an access_token query parameter — https://www.allanninal.dev/github/token-in-query-string/
- rows move between pages and the walk skips records — https://www.allanninal.dev/github/unstable-sort-duplicates/
- a pinned X-GitHub-Api-Version stopped being supported — https://www.allanninal.dev/github/unsupported-api-version/
- a classic token nobody used for a year is deleted for you — https://www.allanninal.dev/github/unused-classic-token-auto-revoked/
- The signature audit reads verified and never reads reason — https://www.allanninal.dev/github/unverified-commit-signature-assumed/
- every request 403s because there is no User-Agent header — https://www.allanninal.dev/github/user-agent-missing/
- the hook sends form-encoded bodies to a JSON receiver — https://www.allanninal.dev/github/webhook-content-type-mismatch/
- webhook deliveries are failing and nobody reads the log — https://www.allanninal.dev/github/webhook-deliveries-failing/
- the hook is not subscribed to the event you are waiting for — https://www.allanninal.dev/github/webhook-event-not-subscribed/
- the webhook posts your payloads to an http:// URL — https://www.allanninal.dev/github/webhook-http-url/
- the webhook exists but somebody switched it off — https://www.allanninal.dev/github/webhook-inactive/
- SSL verification is switched off on the webhook — https://www.allanninal.dev/github/webhook-insecure-ssl/
- a firewall allow-list no longer matches GitHub's hook IPs — https://www.allanninal.dev/github/webhook-ip-allowlist-drift/
- a webhook with no secret sends no signature to verify — https://www.allanninal.dev/github/webhook-no-secret/
- the webhook secret is set and has never been rotated — https://www.allanninal.dev/github/webhook-secret-never-rotated/
- the receiver still checks the legacy SHA-1 signature — https://www.allanninal.dev/github/webhook-sha1-signature-only/
- the receiver takes longer than 10 seconds and times out — https://www.allanninal.dev/github/webhook-timeout-10s/
- the hook subscribes to every event with a wildcard — https://www.allanninal.dev/github/webhook-wildcard-events/
- a JWT sent as token, and the 401 blames the credential — https://www.allanninal.dev/github/wrong-authorization-scheme/
- the automation runs as a person who can leave the company — https://www.allanninal.dev/github/wrong-identity-token/
Each folder holds the same script in Python and in Node.js, plus its test. Set the environment variables named in that folder's README and run it. Nothing writes, so there is no dry run to enable and no flag to be careful about — use a restricted, read-only credential and the worst case is that it tells you nothing is wrong.
MIT. Use it, change it, ship it.