Skip to content

[Bug] parse_repo_name crashes with TypeError when fork repository's owner account was deleted (null owner`) #794

@wmagev

Description

@wmagev

Summary

parse_repo_name in gittensor/utils/utils.py dereferences repo_data["owner"]["login"] without a null check. When a PR's repository has owner: null — which GitHub's GraphQL API returns when the fork owner's account was deleted (ghost-user scenario) — this raises TypeError: 'NoneType' object is not subscriptable. The crash occurs at the top of PullRequest.from_graphql_response, before any per-PR try/except, so the entire PR is silently dropped from scoring.

Location

https://github.com/entrius/gittensor/blob/test/gittensor/utils/utils.py#L9-L11

python def parse_repo_name(repo_data: Dict): """Normalizes and converts repository name from dict""" return f'{repo_data["owner"]["login"]}/{repo_data["name"]}'.lower() ​

Impact

Honest-miner harm: any miner whose PR history includes a fork whose owner later deleted their GitHub account has those PRs (and potentially the entire scoring pass for that miner, depending on call site) silently dropped.

Six call sites (all unguarded):

File Line Risk
gittensor/classes.py 239 Primary crash path — top of from_graphql_response, drops PR from scoring
gittensor/classes.py 422, 430, 436 Log-formatting — shielded in practice by earlier :239 failure
gittensor/utils/github_api_tools.py 887 head_repo and parse_repo_name(head_repo) — the head_repo truthy check only guards head_repo = None, NOT head_repo['owner'] = None, so still crashes
gittensor/utils/github_api_tools.py 983 Inside a try: — caught but noisy

Reproduction

A PR whose headRepository.owner (or repository.owner) is null in GitHub's GraphQL response. This happens when the fork owner's GitHub account has been deleted; GitHub keeps the PR visible but returns null for the repository's owner field.

Why this isn't covered by #537

#537's null-author fix guarded pr_raw['author'] only. The nested repository.owner / headRepository.owner paths were not touched. Same ghost-user pattern, different field.

Suggested fix (~3 lines)

​```python
from typing import Optional

def parse_repo_name(repo_data: Dict) -> Optional[str]:
"""Normalizes and converts repository name from dict.
Returns None if owner is missing (e.g. deleted fork-owner account)."""
owner = (repo_data.get('owner') or {}).get('login')
name = repo_data.get('name')
return f'{owner}/{name}'.lower() if owner and name else None
​```

Call sites need a None-guard on the result — skip the PR / log and continue rather than propagating None into downstream string comparisons. Happy to open a PR if useful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions