{"record":{"id":"871d93ec80f9a564","repo":"666ghj/MiroFish","slug":"github-token-is-missing-or-invalid","errorCode":null,"errorMessage":"GITHUB_TOKEN is missing or invalid","messagePattern":"GITHUB_TOKEN is missing or invalid","errorType":"exception","errorClass":"FetchError","httpStatus":null,"severity":"error","filePath":"scripts/fetch_star_count.py","lineNumber":74,"sourceCode":"def _read_response(response: Any) -> bytes:\n    raw_length = response.headers.get(\"Content-Length\")\n    if raw_length is not None:\n        try:\n            content_length = int(raw_length, 10)\n        except (TypeError, ValueError) as exc:\n            raise FetchError(\"GitHub API returned invalid response metadata\") from exc\n        if content_length < 0 or content_length > MAX_HTTP_BYTES:\n            raise FetchError(\"GitHub API response exceeded the size limit\")\n\n    payload = response.read(MAX_HTTP_BYTES + 1)\n    if len(payload) > MAX_HTTP_BYTES:\n        raise FetchError(\"GitHub API response exceeded the size limit\")\n    return payload\n\n\ndef fetch_star_count(token: str, opener: Any | None = None) -> int:\n    if not token or len(token) > 4_096 or \"\\r\" in token or \"\\n\" in token:\n        raise FetchError(\"GITHUB_TOKEN is missing or invalid\")\n\n    request = urllib.request.Request(\n        API_URL,\n        headers={\n            \"Accept\": \"application/vnd.github+json\",\n            \"Authorization\": f\"Bearer {token}\",\n            \"User-Agent\": \"Repository-Star-History-Fetcher\",\n            \"X-GitHub-Api-Version\": API_VERSION,\n        },\n        method=\"GET\",\n    )\n    client = opener or _build_opener()\n    try:\n        response = client.open(request, timeout=TIMEOUT_SECONDS)\n    except urllib.error.HTTPError as exc:\n        status = exc.code\n        exc.close()\n        raise _status_error(status) from None","sourceCodeStart":56,"sourceCodeEnd":92,"githubUrl":"https://github.com/666ghj/MiroFish/blob/b5b53acc57189a4a42e44a23e149dc655c98fe82/scripts/fetch_star_count.py#L56-L92","documentation":"Input validation at the top of fetch_star_count: the token must be non-empty, at most 4096 characters, and contain no CR or LF. The newline checks exist because the token is interpolated into an Authorization header — an embedded \\r/\\n would allow header injection (request splitting). This is a guard against a malformed GITHUB_TOKEN, not a GitHub-side failure.","triggerScenarios":"GITHUB_TOKEN unset or empty (os.environ.get('GITHUB_TOKEN', '') yields ''); a token with trailing newline/whitespace pasted from a CI secret (the classic trailing \\n in GitHub Actions/GitLab CI secrets); a token longer than 4096 chars; a multiline value accidentally assigned.","commonSituations":"CI scheduled workflow missing the GITHUB_TOKEN secret in env:; secret copied with quotes/newline into .env; runner injecting the variable with surrounding whitespace; passing the wrong env var name.","solutions":["Check the secret wiring: in GitHub Actions confirm the step has env: GITHUB_TOKEN: ${{ secrets.X }} (or uses the auto GITHUB_TOKEN correctly); locally confirm it is exported in the shell running the script.","Strip the value before use: export GITHUB_TOKEN=\"$(printf '%s' \"$GITHUB_TOKEN\" | tr -d '\\r\\n')\" — the script intentionally does not strip, so fix the source of the newline.","Verify length/format: a classic ghp_ PAT is ~40 chars; if yours is >4096 or multiline, the wrong value (e.g. a file path or JSON) was assigned."],"exampleFix":"# before (CI step)\n- run: python scripts/fetch_star_count.py\n  env:\n    GITHUB_TOKEN: ${{ secrets.STAR_HISTORY_TOKEN }}\n# secret stored with trailing newline -> error\n\n# after: sanitize once at the call site (or fix the secret)\n- run: python scripts/fetch_star_count.py\n  env:\n    GITHUB_TOKEN: ${{ secrets.STAR_HISTORY_TOKEN }}\n# and in the wrapper: GITHUB_TOKEN=\"${GITHUB_TOKEN//$'\\n'/}\" python scripts/fetch_star_count.py","handlingStrategy":"validation","validationCode":"import os\n\nTOKEN_MAX = 4_096\n\ntoken = os.environ.get(\"GITHUB_TOKEN\", \"\")\nif not token or len(token) > TOKEN_MAX or \"\\r\" in token or \"\\n\" in token:\n    raise SystemExit(\n        \"GITHUB_TOKEN must be set, <=4096 chars, without newlines \"\n        \"(check the CI secret for a trailing newline)\"\n    )\ncount = fetch_star_count(token)","typeGuard":"def is_valid_github_token(value: object) -> bool:\n    \"\"\"True when value can be safely placed in an Authorization header.\"\"\"\n    return (\n        isinstance(value, str)\n        and 0 < len(value) <= 4_096\n        and \"\\r\" not in value\n        and \"\\n\" not in value\n    )","tryCatchPattern":"try:\n    count = fetch_star_count(os.environ.get(\"GITHUB_TOKEN\", \"\"))\nexcept FetchError as exc:\n    if exc.args[0] == \"GITHUB_TOKEN is missing or invalid\":\n        raise SystemExit(\"CI: add GITHUB_TOKEN to the step env / strip trailing newline\")\n    raise","preventionTips":["Fail fast on the token before any network call — validate shape at the entry point of your wrapper.","When pasting secrets into CI, use the provider's secret store rather than inline YAML to avoid stray newlines.","Log token length (never the token) in debug output to catch truncation or multi-value assignment early."],"tags":["authentication","env-vars","header-injection","fetch-star-count","ci"],"backgroundTag":null,"analyzedSha":"b5b53acc57189a4a42e44a23e149dc655c98fe82","analyzedAt":"2026-08-14T22:29:33.146Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}