Skip to content

fix: avoid trapping on non-finite prefetch TTLs and disable hits for TTL <= 0 - #244

Merged
riteshshukla04 merged 1 commit into
margelo:mainfrom
huytdps13400:fix/prefetch-ttl-validation
Oct 3, 2026
Merged

riteshshukla04 merged 1 commit into
margelo:mainfrom
huytdps13400:fix/prefetch-ttl-validation

Conversation

@huytdps13400

Copy link
Copy Markdown
Contributor

Fixes #196

Root cause

  • iOS crash: the three FetchCache.getResultIfFresh call sites in HybridNitroFetchClient.swift converted the TTL with Int64(req.prefetchCacheTtlMs ?? 5_000). Swift's Int64(Double) traps on NaN, ±Infinity and values outside the Int64 range. typeof NaN === 'number', so prefetchCacheTtlMs: NaN or Infinity passed from JS (or an NSNumber passed to NitroAutoPrefetcher.registerPrefetch) crashes the app. Android's Double.toLong() saturates instead, so it doesn't crash there.
  • Zero TTL can still hit: both caches checked only age <= maxAgeMs. When the response completed in the same millisecond, age is 0, so a TTL of 0 returned a hit. The docs say a value <= 0 disables cache hits. On Android, NaN.toLong() is 0, so a NaN TTL hit the same way.

Changes

  • ios/FetchCache.swift: getResultIfFresh now takes maxAgeMs: Double and compares Double(age) <= maxAgeMs, so there's no trapping conversion. It also requires maxAgeMs > 0. The three call sites pass req.prefetchCacheTtlMs ?? 5_000 directly.
  • android/.../FetchCache.kt: getResultIfFresh and hasFreshResult also require maxAgeMs > 0.
  • docs-website/docs/prefetch.md: I removed the parenthetical that described the old age <= maxAgeMs mechanism.

After this change both platforms handle unusual TTLs the same way: <= 0, NaN and -Infinity never hit, Infinity (and huge values) never expire, and ordinary TTLs work as before. Joining an in-flight prefetch still works whatever the TTL. The JS builders are unchanged.

Verification

I couldn't drive the full prefetch flow on a device in this environment. Instead I tested the real FetchCache.swift and FetchCache.kt files in small standalone harnesses, with a stub NitroResponse and the same call-site expressions:

TTL iOS before iOS after Android before Android after
0 (same ms) hit miss hit miss
NaN crash (SIGTRAP, exit 133) miss hit miss
Infinity crash (SIGTRAP, exit 133) hit hit hit
1e19 crash (SIGTRAP, exit 133) hit n/a n/a
-5 / -1 miss miss miss miss
60000 hit hit hit hit

Other checks:

  • ./gradlew :react-native-nitro-fetch:compileDebugKotlin in example/android passes.
  • xcodebuild -scheme NitroFetch -sdk iphonesimulator (Debug, arm64) for the NitroFetch pod in the example workspace succeeds.
  • bun typecheck, bun lint (0 errors) and bun test all pass.

Platforms: iOS (crash and zero-TTL fix) and Android (zero-TTL fix).

This fix was prepared with AI assistance (Claude Code) and verified locally with the tests above.

🤖 Generated with Claude Code

…TTL <= 0

On iOS, Int64(prefetchCacheTtlMs) traps for NaN, Infinity and
out-of-range values. Compare the TTL as a Double instead. Both caches
now also require a positive TTL, so a zero TTL no longer hits when the
response completed in the same millisecond.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Oct 2, 2026

Copy link
Copy Markdown

@huytdps13400 is attempting to deploy a commit to the Margelo Team on Vercel.

A member of the Team first needs to authorize it.

@riteshshukla04

Copy link
Copy Markdown
Member

Thanks

@riteshshukla04
riteshshukla04 merged commit aad957e into margelo:main Oct 3, 2026
7 of 8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Prefetch TTL validation can crash Swift and does not consistently disable zero-TTL hits

2 participants