Fix KeyError when spot token index is missing from tokens list - #298
Fix KeyError when spot token index is missing from tokens list#298RWCS-LTD wants to merge 1 commit into
Conversation
When the HL API briefly returns a universe pair whose token index is not present in spot_meta["tokens"], Info.__init__ raises KeyError and every client crashes at startup. Observed in production 2026-05-29: HL added spot pair @367 referencing token index 479 while the tokens list held 464 entries. This took down every running trader/poller using this SDK for ~54 minutes until HL repaired the response shape. The fix: - Skip universe pairs whose base or quote indices are not in `token_by_index`. This lets Info() initialize for all valid pairs and keeps clients alive through any future universe/tokens drift on the API side. New valid pairs come back into scope automatically as soon as their tokens land in the list. tests/test_info_spot_tokens.py reproduces the scenario with a synthetic spot_meta and asserts Info() no longer raises while still registering the valid pair.
|
Production data point in support of this fix. We have run exactly this skip in place — Two observations that may help the maintainers decide:
Happy to contribute an additional regression test (an index-out-of-range pair alongside valid ones) if that would help this land. AI-assisted. |
Summary
Info.__init__raisesKeyError(orIndexErroron pre-dict-refactor versions) and every client crashes at startup when the HL API briefly returns a universe pair whose token index is not present inspot_meta["tokens"].Reproducer
Observed in production on 2026-05-29: HL added spot pair
@367referencing token index 479 while the tokens list held 464 entries. EveryInfo()caller crashed for ~54 minutes until HL repaired the response shape.Quick repro against live API at the time of the incident:
```python
import json, urllib.request
body = json.dumps({"type": "spotMeta"}).encode()
r = urllib.request.urlopen(urllib.request.Request("https://api.hyperliquid.xyz/info", data=body, headers={"Content-Type": "application/json"}), timeout=10)
spot_meta = json.loads(r.read())
n = len(spot_meta["tokens"])
bad = [(u["name"], u["tokens"]) for u in spot_meta["universe"] if max(u["tokens"]) >= n]
print(f"Pairs with out-of-range tokens: {bad}")
Pairs with out-of-range tokens: [('@367', [479, 0])]
```
Fix
Skip universe pairs whose
baseorquoteis not intoken_by_index:```python
if base not in token_by_index or quote not in token_by_index:
continue
```
This lets
Info()initialize for all valid pairs and keeps clients alive through any future universe/tokens drift on the API side. New valid pairs come back into scope automatically as soon as their tokens land in the list.Test
Added `tests/test_info_spot_tokens.py` with a synthetic `spot_meta` that includes one valid pair and one pair pointing at a non-existent token index. The test asserts `Info()` no longer raises and that the valid pair is still registered.
Verified the test fails on `master` without the patch (`KeyError: 5`) and passes with it.
Test plan