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

  1. Pass the token explicitly to the constructor (token=..., token_source=...) when driving the client directly.
  2. Or export GH_TOKEN / GITHUB_TOKEN before the process starts.
  3. 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 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


AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15). Data as JSON: /api/errors/203dfbf9a0335609. Report an issue: GitHub.