headroomlabs-ai/headroom · error · ValueError

Copilot OAuth token must not be empty.

Error message

Copilot OAuth token must not be empty.

What it means

save_headroom_copilot_oauth_token() strips the incoming token and raises ValueError if nothing remains. This is the persistence step for the token returned by GitHub's device login flow, and the guard prevents writing an auth file whose 'refresh' field is an empty string — which would later be silently unusable. The file (under headroom_copilot_auth_path()) is only written after the check plus a parent mkdir.

Source

Thrown at headroom/copilot_auth.py:499

        logger.debug("Unable to read Headroom Copilot auth file: %s", exc)
        return None

    if not isinstance(payload, dict) or payload.get("type") != "oauth":
        return None
    token = payload.get("refresh")
    return token.strip() if isinstance(token, str) and token.strip() else None


def save_headroom_copilot_oauth_token(
    token: str,
    *,
    domain: str = DEFAULT_GITHUB_HOST,
) -> Path:
    """Persist the Copilot OAuth token returned by GitHub device login."""

    token = token.strip()
    if not token:
        raise ValueError("Copilot OAuth token must not be empty.")

    path = headroom_copilot_auth_path()
    path.parent.mkdir(parents=True, exist_ok=True)
    body: dict[str, Any] = {
        "type": "oauth",
        "provider": "github-copilot",
        "refresh": token,
        "domain": _github_oauth_domain(domain),
        "created_at": int(time.time()),
    }
    path.write_text(json.dumps(body, indent=2, sort_keys=True) + "\n", encoding="utf-8")
    try:
        path.chmod(0o600)
    except OSError:
        pass
    return path

View on GitHub (pinned to 322425c43b)

Solutions

  1. Validate before saving: check `token and token.strip()` and surface a meaningful auth error to the user
  2. Trace where the token came from — usually poll_copilot_device_authorization returned successfully but the caller stored the wrong field
  3. Never persist empty credentials; re-run the device authorization flow to obtain a real token

Example fix

# before
token = payload.get("access_token") or ""
save_headroom_copilot_oauth_token(token)  # ValueError

# after
token = (payload.get("access_token") or "").strip()
if not token:
    raise RuntimeError("Device flow returned no access token")
save_headroom_copilot_oauth_token(token)
Defensive patterns

Strategy: validation

Validate before calling

token = (token or "").strip()
if not token:
    raise ValueError("Refusing to save empty Copilot OAuth token — re-run device login")

Type guard

def is_valid_oauth_token(value: object) -> bool:
    return isinstance(value, str) and len(value.strip()) > 0

Try / catch

try:
    save_headroom_copilot_oauth_token(token)
except ValueError:
    # empty credential: never retry with the same value; restart the device flow
    raise RuntimeError("Device flow produced no token; restart authorization")

Prevention

When it happens

Trigger: Calling save_headroom_copilot_oauth_token(token) with an empty string, a whitespace-only string, or None coerced to str. In practice this happens when a caller passes an unvalidated value extracted from an OAuth payload where the token field was absent.

Common situations: Wiring a custom GitHub device-flow client and forwarding payload.get('access_token') (None) directly; copy-paste scripts that read a token from an env var that was never set (os.environ.get gives None or ''); upstream API change renaming the token field so extraction yields ''.

Related errors


AI-assisted analysis of headroomlabs-ai/headroom@322425c43b (2026-08-15). Data as JSON: /api/errors/b699a5a4b3b633e7. Report an issue: GitHub.