Skip to content
This repository was archived by the owner on Jul 10, 2026. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions bittensor/core/async_subtensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -1046,6 +1046,14 @@ async def all_subnets(
return_exceptions=True,
)

if isinstance(decoded, Exception):
# ``return_exceptions=True`` surfaces a failed runtime call as the
# ``decoded`` value; iterating it below would raise TypeError.
logging.warning(
f"Unable to fetch all subnets info for block {block}, block hash {block_hash}: {decoded}"
)
return None

if not isinstance(subnet_prices, (SubstrateRequestException, ValueError)):
for sn in decoded:
sn.update(
Expand Down Expand Up @@ -1409,6 +1417,13 @@ async def get_all_subnets_info(
),
return_exceptions=True,
)
if isinstance(result, Exception):
# ``return_exceptions=True`` surfaces a failed runtime call as the
# ``result`` value; SubnetInfo.list_from_dicts would then raise TypeError.
logging.warning(
f"Unable to fetch all subnets info for block {block}, block hash {block_hash}: {result}"
)
return []
if not result:
return []

Expand Down
7 changes: 7 additions & 0 deletions mypy.ini
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
[mypy]
ignore_missing_imports = True
ignore_errors = True
follow_imports_for_stubs = True

[mypy-numpy.*]
follow_imports = skip

[mypy-numpy]
follow_imports = skip

[mypy-*.axon.*]
ignore_errors = False
Expand Down
39 changes: 39 additions & 0 deletions tests/unit_tests/test_async_subtensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -2819,6 +2819,27 @@ async def test_get_all_subnets_info_success(mocker, subtensor):
)


@pytest.mark.asyncio
async def test_get_all_subnets_info_returns_empty_on_rpc_error(mocker, subtensor):
"""If the runtime call fails, gather(return_exceptions=True) yields the exception as
``result``; get_all_subnets_info must return [] instead of crashing inside
SubnetInfo.list_from_dicts with a TypeError."""
mocker.patch.object(subtensor, "get_subnet_prices", return_value={})
mocker.patch.object(
subtensor,
"query_runtime_api",
side_effect=async_subtensor.SubstrateRequestException("rpc down"),
)
mocked_list_from_dicts = mocker.patch.object(
async_subtensor.SubnetInfo, "list_from_dicts"
)

result = await subtensor.get_all_subnets_info()

assert result == []
mocked_list_from_dicts.assert_not_called()


@pytest.mark.asyncio
async def test_set_subnet_identity(mocker, subtensor, fake_wallet):
"""Verify that subtensor method `set_subnet_identity` calls proper function with proper arguments."""
Expand Down Expand Up @@ -3768,6 +3789,24 @@ async def test_all_subnets(subtensor, mocker):
assert result == mocked_di_list_from_dicts.return_value


@pytest.mark.asyncio
async def test_all_subnets_returns_none_on_rpc_error(subtensor, mocker):
"""If the runtime call fails, gather(return_exceptions=True) yields the exception as
``decoded``; all_subnets must return None (its documented failure value) instead of
raising TypeError while trying to iterate the exception."""
mocker.patch.object(subtensor, "determine_block_hash")
mocker.patch.object(subtensor, "get_subnet_prices", return_value={})
mocker.patch.object(
subtensor.substrate,
"runtime_call",
side_effect=async_subtensor.SubstrateRequestException("rpc down"),
)

result = await subtensor.all_subnets()

assert result is None


@pytest.mark.asyncio
async def test_subnet(subtensor, mocker):
"""Verify that `subnet` calls proper methods and returns the correct value."""
Expand Down