unslothai/unsloth · critical · RuntimeError
GH_TOKEN or GITHUB_TOKEN not set in environment
Error message
GH_TOKEN or GITHUB_TOKEN not set in environment
What it means
RuntimeError raised in the constructor of the GitHub client (gh_client) when no token is available: no explicit token argument, no GH_TOKEN, no GITHUB_TOKEN. Unlike scraper.py's ValueError (error 969) this is the low-level client failing at instantiation — it cannot build an authenticated session without a bearer token, and anonymous GraphQL access is not possible.
Source
Thrown at studio/backend/plugins/data-designer-github-repo-seed/src/data_designer_github_repo_seed/scraper_impl/gh_client.py:71
class GitHubClient:
def __init__(
self,
min_remaining_graphql: int = 100,
min_remaining_rest: int = 100,
token: str | None = None,
token_source: str | None = None,
):
if token:
self._token_source = token_source or "explicit token argument (recipe-level field)"
elif os.environ.get("GH_TOKEN"):
self._token_source = "GH_TOKEN environment variable"
token = os.environ["GH_TOKEN"]
elif os.environ.get("GITHUB_TOKEN"):
self._token_source = "GITHUB_TOKEN environment variable"
token = os.environ["GITHUB_TOKEN"]
else:
raise RuntimeError("GH_TOKEN or GITHUB_TOKEN not set in environment")
self.session = requests.Session()
self.session.headers.update({**BASE_HEADERS, "Authorization": f"Bearer {token}"})
self.min_remaining_graphql = min_remaining_graphql
self.min_remaining_rest = min_remaining_rest
self.graphql_remaining: Optional[int] = None
self.graphql_reset: Optional[int] = None
self.rest_remaining: Optional[int] = None
self.rest_reset: Optional[int] = None
self.calls_graphql = 0
self.calls_rest = 0
self.retry_count = 0
def _sleep_until(
self,
reset_ts: int,
buffer_s: int = 10,
) -> None:
now = int(time.time())View on GitHub (pinned to 203007d190)
Solutions
- Pass the token explicitly to the constructor (token=..., token_source=...) when driving the client directly.
- Or export GH_TOKEN / GITHUB_TOKEN before the process starts.
- In tests, set a dummy env var or inject a stub session rather than relying on real credentials.
Example fix
# before client = GitHubClient() # RuntimeError: GH_TOKEN or GITHUB_TOKEN not set # after client = GitHubClient(token=os.environ["MY_TOKEN"], token_source="MY_TOKEN env")
Defensive patterns
Strategy: validation
Validate before calling
import os
def can_construct_client(explicit_token: str | None = None) -> bool:
return bool(explicit_token or os.environ.get("GH_TOKEN") or os.environ.get("GITHUB_TOKEN"))
assert can_construct_client(), "set GH_TOKEN before creating the client" Prevention
- When using scraper_impl directly, always pass token explicitly rather than relying on env.
- In tests, monkeypatch GH_TOKEN to a dummy value before instantiating the client.
When it happens
Trigger: Constructing the client class directly (e.g. GitHubClient()) without arguments in an environment lacking GH_TOKEN/GITHUB_TOKEN, or the scraper path reaching client construction with token resolution having returned nothing.
Common situations: Unit tests instantiating the client without mocking env vars; scripts that import the internal scraper_impl module and build the client themselves; cron/systemd services missing Environment=GH_TOKEN=... definitions.
Related errors
- GitHub token is required. Set it in the recipe config or the
- GitHub {endpoint} returned {r.status_code} {r.reason}. Token
- Invalid or expired token
- Invalid or expired API key
- Invalid token payload
AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15).
Data as JSON: /api/errors/203dfbf9a0335609.
Report an issue: GitHub.