xtekky/gpt4free · error · ValueError

device_code is required for polling

Error message

device_code is required for polling

What it means

A plain ValueError raised at the top of GithubCopilot.oauth_poll when the caller passes an empty/None device_code. The device-code polling endpoint (RFC 8628) requires the code returned by the initial device authorization response (oauth_begin), so an empty value is rejected immediately before any network call.

Source

Thrown at g4f/Provider/github/GithubCopilot.py:267

            "verification_uri", "https://github.com/login/device"
        )
        user_code = device_auth.get("user_code")
        device_code = device_auth.get("device_code")

        return {
            "status": "pending",
            "verification_uri": verification_uri,
            "user_code": user_code,
            "device_code": device_code,
            "expires_in": device_auth.get("expires_in"),
            "interval": device_auth.get("interval", 5),
        }

    @classmethod
    async def oauth_poll(cls, device_code: str):
        """Poll GitHub token endpoint; save credentials once access_token is available."""
        if not device_code:
            raise ValueError("device_code is required for polling")

        client = GithubOAuth2Client()
        token_response = await client.pollDeviceToken({"device_code": device_code})

        if token_response.get("status") == "pending":
            return {"status": "pending", "message": "Authorization pending"}

        if token_response.get("access_token"):
            credentials = {
                "access_token": token_response["access_token"],
                "token_type": token_response.get("token_type", "bearer"),
                "scope": token_response.get("scope", ""),
                "expiry_date": int(time.time() * 1000) + (365 * 24 * 60 * 60 * 1000),
            }
            await client.sharedManager.saveCredentialsToFile(credentials)
            return {"status": "success", "message": "GitHub Copilot OAuth successful"}

        return {

View on GitHub (pinned to 973504e177)

Solutions

  1. Pass the 'device_code' string from the dict returned by oauth_begin(), not the whole dict or the user_code
  2. Persist the device_code (with its expires_in) between the begin and poll steps if the flow spans processes
  3. Guard with 'if not device_code: restart the flow' before calling oauth_poll

Example fix

# before
result = await GithubCopilot.oauth_poll(auth_response)
# after
result = await GithubCopilot.oauth_poll(auth_response["device_code"])
Defensive patterns

Strategy: validation

Validate before calling

if not isinstance(device_code, str) or not device_code.strip():
    raise ValueError("restart device flow: no device_code")
result = await GithubCopilot.oauth_poll(device_code.strip())

Type guard

def is_device_code(code: object) -> bool:
    return isinstance(code, str) and len(code) > 20 and code.isalnum()

Try / catch

try:
    result = await GithubCopilot.oauth_poll(device_code)
except ValueError as e:
    if 'device_code is required' in str(e):
        auth = await GithubCopilot.oauth_begin()  # restart flow
        device_code = auth["device_code"]

Prevention

When it happens

Trigger: Calling oauth_poll('') or oauth_poll(None); losing the device_code between the oauth_begin response and the poll step (e.g. CLI restart, serialization bug, passing the whole response dict instead of its 'device_code' field).

Common situations: Custom auth orchestration code that stores the wrong field from the device authorization response; retry logic that re-enters polling without persisting the device_code.

Related errors


AI-assisted analysis of xtekky/gpt4free@973504e177 (2026-08-14). Data as JSON: /api/errors/8e1ee102ab57c1a2. Report an issue: GitHub.