About this guide: I'm Lawrence, the writer behind supa.is. Between February and May 2026 I've published 150+ articles on supa.is across crypto and brokerage tooling โ including 30+ Hyperliquid-specific guides (recent examples: Hyperliquid Python SDK Tutorial: Build a Bot (2026), Interactive Brokers Python API Error Handling: Codes & Fixes (2026), IB Python API Connection Timeout: 8 Common Fixes (2026)). The most-repeated reader question across that Hyperliquid archive is exactly how to stop the Python SDK from crashing when the spot asset list changes between calls, which is why I'm publishing this standardized guide instead of answering one-off.
If your Hyperliquid bot dies with an index error, a shape mismatch, or a ValueError the moment it zips spotMetaAndAssetCtxs against a list you built earlier, you are not doing anything wrong. The API is working as designed. Your code is assuming the spot market is a fixed table. It is not.
This guide walks through the exact failure mode reported in hyperliquid-python-sdk issue #308, explains the mechanism behind it, and gives you the defensive patterns that keep a bot alive across listing and delisting events. It is written for developers who already run the Python SDK against the Hyperliquid spot API and are hitting this specific array length mismatch.
What the reported failure looks like
In issue #308, a developer found that the spotMetaAndAssetCtxs response contained 320 spot assets while their code expected 711. The first 70 positions lined up perfectly between the two arrays, then everything after that diverged.
That detail matters more than the raw numbers. When two arrays share a clean prefix and then fall out of sync, you have three candidate explanations:
- The source data changed between two calls. Your reference list was captured at time T0. The API response you are comparing against was captured at T1. Between T0 and T1, assets were added or removed, and the ordering shifted.
- You are comparing two different slices of the same response. The meta array and the asset context array in a single
spotMetaAndAssetCtxscall are paired by index. If you ever flatten, filter, or re-sort one of them independently, you break the pairing. - You are comparing against a stale cached list. A list you persisted to disk or a database from a previous run will drift from the live API as soon as the spot market changes.
Whatever the exact history of your two lists, the fix is the same: never treat the spot asset list as a constant. Treat it as a live, mutable resource that you re-fetch and re-validate on every use.
Why the array length changes at all
Hyperliquid spot is a real market with listings and delistings. When a new spot pair goes live, it appears in the meta array and gets a corresponding asset context entry. When a pair is delisted or paused, it drops out. The spotMetaAndAssetCtxs endpoint returns the current state of that market, not a historical snapshot.
This is the same reason a stock exchange's symbol list changes when companies IPO or get delisted. If you wrote a trading system that hardcoded the S&P 500 constituents from 2020, it would break every time the index rebalanced. The Hyperliquid spot list behaves the same way, except the changes can happen faster and without a scheduled rebalance date.
Two practical consequences follow:
- Index-based access is fragile.
meta[i]andctxs[i]are paired *within a single response*. Across responses, indeximay point to a different asset. - Length-based assumptions are fragile.
len(meta) == len(ctxs)holds within one response, butlen(meta)itself changes over time.
The defensive patterns that fix it
Here are the patterns, in order of how much they help.
1. Re-fetch the list on every cycle, not at startup
The single biggest win. If your bot calls spotMetaAndAssetCtxs once at startup and caches the result, you are guaranteed to drift. Call it on every trading cycle (or at least on a short interval) and use the fresh response for all index math in that cycle.
2. Pair by index within one response, never across responses
When you receive a response, treat meta and ctxs as a single unit. Iterate with zip(meta, ctxs) and never store one of them separately for use against a future response. If you need to persist state, persist asset identity (the symbol or index name), not the positional index.
3. Key by symbol, not by position
Build a dictionary keyed by the asset's symbol (or the stable identifier in the meta entry) instead of relying on list position. When you need the context for "PEPE", look it up by symbol in the current response. If the symbol is not present, it was delisted or never existed in this response, and you handle that explicitly.
4. Validate length and prefix before you trust the data
Before iterating, assert that len(meta) == len(ctxs) for the response you just received. If they differ, something is wrong with the response itself (rare, but log it and back off). Separately, if you are comparing against a previous response for change detection, compare sets of symbols, not array lengths or positional alignment.
5. Handle the "asset disappeared" case explicitly
Your strategy logic should answer: *what do I do if the asset I was tracking is no longer in the response?* The honest answer is usually "stop trading it, flatten if you have a position, and alert." Do not let this fall through into an IndexError.
A sketch of the resilient loop (illustrative, not a tested workflow):
def process_cycle(client):
meta, ctxs = client.spot_meta_and_asset_ctxs()
if len(meta) != len(ctxs):
log.error("meta/ctxs length mismatch: %d vs %d", len(meta), len(ctxs))
return
current = {m["name"]: c for m, c in zip(meta, ctxs)}
for tracked in tracked_symbols:
if tracked not in current:
handle_delisting(tracked)
continue
# use current[tracked] for strategy logic
Like what you're reading? Try it yourself โ this link supports ChartedTrader at no cost to you.
Join Hyperliquid and get a 4% fee discount on your first $25M of volume โ
The key idea: current is rebuilt from scratch every cycle. Nothing from the previous cycle is trusted by position.
Common pitfalls when using spotMetaAndAssetCtxs
A few mistakes show up repeatedly in the wild.
Caching the list "just for a few minutes." A few minutes is long enough for a listing or delisting to land. If you must cache, cache with a very short TTL and always re-validate the length on use. Sorting one array but not the other. If you sortmeta by name for display, you have silently broken the pairing with ctxs. Sort a list of tuples, or sort both by the same key, or better, build the symbol-keyed dictionary and sort the dictionary's items.
Assuming the 0โ70 prefix is stable forever. The prefix alignment in the reported issue was a snapshot-in-time observation. It is not a guarantee. Do not write code that says "the first 70 are always the same."
Comparing against a hardcoded list in source control. A list of "all Hyperliquid spot tokens" committed to your repo will be wrong the day after you push it. Generate it at runtime.
Treating a length change as an API bug. It is not. The API is telling you the truth about the current market. Your job is to adapt, not to file a bug report.
How this differs from a generic API error
It is worth separating this failure mode from the ones covered by the Hyperliquid error responses documentation. A generic API error (authentication failure, rate limit, invalid request) comes back as an explicit error object, and your retry/backoff logic handles it. The array length mismatch comes back as a successful response that your code misinterprets. That makes it sneakier: your HTTP status is 200, your request "succeeded," and the crash happens three lines later in your own index math.
If you are building broader error handling for your Hyperliquid bot, the Hyperliquid Python SDK Tutorial: Build a Bot (2026) covers the general structure, and the IBKR Python API Error Handling: Codes & Fixes (2026) guide is a useful cross-reference for how to think about error taxonomy in a brokerage API, even though the specific codes differ. The principle is shared: classify the failure, then apply the matching recovery. For the array mismatch, the recovery is "re-fetch and re-key," not "retry."
When to escalate to the SDK maintainers
Most array length mismatch reports are the stale-list problem described above, and the fix is in your code. But there is a narrow case where the issue belongs upstream: if meta and ctxs within the same single response have different lengths, that is a genuine API/SDK inconsistency, not drift. That is the case worth raising in the hyperliquid-python-sdk repository, with a minimal repro: the exact request, the response payload (redact sensitive fields), and the lengths you observed.
If your report is "my cached list has 711 and the live response has 320," the maintainers will (correctly) point you back to the stale-list explanation. Frame your issue around the within-response invariant, or it will be closed as user error.
Practical checklist before you ship
Run through this list before you consider the fix done:
- [ ]
spotMetaAndAssetCtxsis called on every cycle, not cached at startup. - [ ]
metaandctxsare paired withzipwithin a single response only. - [ ] State is keyed by symbol/identifier, never by positional index.
- [ ] A length assertion (
len(meta) == len(ctxs)) guards every response. - [ ] A delisted asset triggers an explicit handler, not an
IndexError. - [ ] Change detection compares symbol sets, not array lengths.
- [ ] You can reproduce the fix against a response where the asset count differs from your previous run.
FAQ
Why does spotMetaAndAssetCtxs return a different number of assets each time?
Because Hyperliquid spot has live listings and delistings. The endpoint returns the current market state, so the count changes whenever a pair is added or removed. Treat the list as mutable, not constant.
Is a 320 vs 711 mismatch an API bug?
No. As reported in issue #308, the gap reflects a stale reference list versus the live response. The API returned a valid current snapshot. Only a length mismatch *within a single response* would indicate a real API/SDK problem.
How do I stop my bot from crashing when an asset is delisted?
Key your state by symbol and rebuild the mapping from each fresh response. If a tracked symbol is missing from the current response, route it to an explicit delisting handler (flatten and alert) instead of letting it hit an index error.
Should I cache the spot asset list at all?
Avoid long-lived caches. If you must cache, use a very short TTL and re-validate the array length on every use. The safest pattern is to re-fetch on each trading cycle and pair meta with ctxs only within that response.
Where do I report a real inconsistency in the SDK?
Open an issue in the hyperliquid-python-sdk repository with a minimal repro showing meta and ctxs having different lengths in the same response. Reports about cached-list drift will be directed back to the stale-list fix.
Risk Warning
Risk Warning: Crypto trading involves substantial risk of loss. Never invest more than you can afford to lose. This is not financial advice.
---
If you are running a Hyperliquid bot and want the fee side of the equation under control, join Hyperliquid through the link above to get a 4% fee discount on your first $25M of volume (Vaults and sub-accounts are excluded from the discount). Lower fees mean the array-mismatch fix and the rest of your defensive code actually pay for themselves.
Continue with Hyperliquid
Browse the Hyperliquid guide hub for the complete user journey.