Description
Several GraphQL parsers in gittensor/utils/github_api_tools.py use the pattern obj.get('field', {}).get('nested'). In Python, .get('field', {}) does not fall back to {} when the key exists with value null — it returns None, and the next .get(...) raises AttributeError.
GitHub GraphQL commonly returns explicit null for nullable nested objects (e.g. "data": null, "repository": null, "timelineItems": null).
The same file already uses the correct null-safe pattern elsewhere — e.g. _closing_issue_numbers_for_repo uses (closing_ref or {}).get('nodes'), and gittensor/validator/pat_handler.py uses (data.get('data') or {}).get('viewer').
Affected code
| Location |
Unsafe line |
Crash when |
_search_issue_referencing_prs_graphql |
result.get('data', {}).get('repository', {}).get('issue') (line ~281) |
GraphQL body has "data": null or "repository": null |
_search_issue_referencing_prs_graphql |
issue_data.get('timelineItems', {}).get('nodes', []) (line ~286) |
Issue payload has "timelineItems": null |
_select_current_close_event |
issue_data.get('timelineItems', {}).get('nodes', []) or [] (line ~414) |
Same — trailing or [] does not help once .get('nodes') is invoked on None |
find_solver_from_closure_event |
result.get('data', {}).get('repository', {}).get('issue') (line ~491) |
Same as first row |
Steps to Reproduce
- Mock
execute_graphql_query to return a syntactically valid GraphQL 200 response such as:
{"data": {"repository": {"issue": {"timelineItems": null, "closedAt": "2025-06-01T00:00:00Z"}}}}
- Call
_search_issue_referencing_prs_graphql(...) or find_solver_from_closure_event(...).
- Observe
AttributeError: 'NoneType' object has no attribute 'get' instead of a graceful None / empty result.
Expected Behavior
Null nested GraphQL fields should be treated as missing/empty — same as the (obj or {}) pattern used in pat_handler.py and _closing_issue_numbers_for_repo.
Actual Behavior
Unhandled AttributeError aborts issue cross-reference search and solver lookup.
Suggested Fix
Replace unsafe chains with null-safe access, e.g.:
issue_data = ((result.get('data') or {}).get('repository') or {}).get('issue')
timeline_nodes = (issue_data.get('timelineItems') or {}).get('nodes', [])
Each line is a one-character-class change (or {} instead of , {} default).
Impact
- Issue-bounty solver attribution (
find_solver_from_closure_event)
- CLI / contract paths that search PRs referencing an issue (
_search_issue_referencing_prs_graphql)
- Any operator log showing an unexpected traceback instead of the existing warning paths
Environment
- Branch:
test
- File:
gittensor/utils/github_api_tools.py
Description
Several GraphQL parsers in
gittensor/utils/github_api_tools.pyuse the patternobj.get('field', {}).get('nested'). In Python,.get('field', {})does not fall back to{}when the key exists with valuenull— it returnsNone, and the next.get(...)raisesAttributeError.GitHub GraphQL commonly returns explicit
nullfor nullable nested objects (e.g."data": null,"repository": null,"timelineItems": null).The same file already uses the correct null-safe pattern elsewhere — e.g.
_closing_issue_numbers_for_repouses(closing_ref or {}).get('nodes'), andgittensor/validator/pat_handler.pyuses(data.get('data') or {}).get('viewer').Affected code
_search_issue_referencing_prs_graphqlresult.get('data', {}).get('repository', {}).get('issue')(line ~281)"data": nullor"repository": null_search_issue_referencing_prs_graphqlissue_data.get('timelineItems', {}).get('nodes', [])(line ~286)"timelineItems": null_select_current_close_eventissue_data.get('timelineItems', {}).get('nodes', []) or [](line ~414)or []does not help once.get('nodes')is invoked onNonefind_solver_from_closure_eventresult.get('data', {}).get('repository', {}).get('issue')(line ~491)Steps to Reproduce
execute_graphql_queryto return a syntactically valid GraphQL 200 response such as:{"data": {"repository": {"issue": {"timelineItems": null, "closedAt": "2025-06-01T00:00:00Z"}}}}_search_issue_referencing_prs_graphql(...)orfind_solver_from_closure_event(...).AttributeError: 'NoneType' object has no attribute 'get'instead of a gracefulNone/ empty result.Expected Behavior
Null nested GraphQL fields should be treated as missing/empty — same as the
(obj or {})pattern used inpat_handler.pyand_closing_issue_numbers_for_repo.Actual Behavior
Unhandled
AttributeErroraborts issue cross-reference search and solver lookup.Suggested Fix
Replace unsafe chains with null-safe access, e.g.:
Each line is a one-character-class change (
or {}instead of, {}default).Impact
find_solver_from_closure_event)_search_issue_referencing_prs_graphql)Environment
testgittensor/utils/github_api_tools.py