The LI.FI Earn API has 24 known pitfalls that cause failures, data corruption, or silent
bugs. Pitfalls 1-14 originate from LI.FI’s official integration guide. Pitfalls 15-24 were
found by probing the live API: several of them only after they had already broken something
in production.
EarnForge eliminates every single one at the SDK layer. Downstream surfaces (CLI, React,
MCP, Studio) never need to think about them.
This pitfall inverted in April 2026. It previously said the Earn Data API
takes no auth header. It now hard-401s without one, and LI.FI’s general API
docs still say no key is required, which remains true only for li.quest.
Root Cause
earn.li.fi returns 401 Missing x-lifi-api-key header on every endpoint. Code written against the old behaviour fails everywhere at once.
SDK Mitigation
EarnDataClient requires a key and throws MissingApiKeyError naming the portal, rather than surfacing a bare 401.
This pitfall also inverted. The OpenAPI spec still documents a string.
Root Cause
analytics.tvl.usd was "12345678.90" and is now 12345678.90. Code calling .split() or expecting a string crashes; code written for the spec is wrong today.
SDK Mitigation
parseTvl() accepts either and returns { raw, parsed, bigint }, so a flip back in either direction is a non-event.
Short-lived or freshly deployed vaults return apy1d: null and sometimes apy7d: null. Code that divides by apy1d gets Infinity.
SDK Mitigation
AnalyticsSchema types all three as number | null. getBestApy() fallback chain skips nulls. riskScore() handles missing historical data with a moderate default.
Test
pitfall-18-apy1d-null.test.ts
Pitfall 19 – Stale protocol slugs return zero results
Protocol ids became unversioned. protocol=morpho-v1 returns HTTP 200 with an empty list (no error) so a stale slug is indistinguishable from a protocol with no vaults. LI.FI’s own MCP server still advertises the versioned ids.
SDK Mitigation
Ids are resolved from /v1/protocols; a live test asserts every id EarnForge ships still exists upstream.
Unrecognised params are dropped silently. minTvl instead of minTvlUsd returns the entire unfiltered fleet, so a “$100M+ TVL” filter appears to work and filters nothing.
SDK Mitigation
The client sends minTvlUsd, and a test asserts the emitted URL rather than the response.
Present on every vault, in no spec or changelog, flagging ~9% as suspect: mostly zero_apy, occasionally apy_outlier. Nothing surfaces it, so flagged vaults look like any other.
SDK Mitigation
A weighted risk dimension. A flagged vault caps at 7.96 and can never read as low risk; suggest() excludes them unless includeFlagged: true.
The OpenAPI spec is wrong in six places and documents fields no vault sends. Following it produces wrong values rather than type errors. The APY case overstates every yield 100×.
SDK Mitigation
detectDrift() diffs live vs spec vs schema and reports which pair disagrees. CI runs it daily.
Pitfall 15 anticipated an emptyunderlyingTokens array. What the API actually sends is stranger: a populated array whose entries carry only an address, with no symbol and no decimals. One vault in the fleet did this, and because the schema required both fields, listAll() threw partway through. The Studio’s vault list read zero in production and earnforge list could not complete without a chain filter.
SDK Mitigation
symbol and decimals are optional on the token schema. Consumers that need them resolve on-chain or skip the vault, rather than the whole fleet iteration dying on one entry.