← Back to list

The Vercel KV Cache Mystery: When Your Data Exists But Returns Null

How I discovered and fixed a sneaky type inference bug in Vercel KV that was breaking my Daily Melody app’s caching

Ariel Parra · 2025-07-21 21:58 · 5 claps · 3.3 min read
#vercel #kv-storage #typescript
Open on Medium ↗
Wiki topics: RAG · RAG & Retrieval OPS · LLMOps & Inference 🌐 · Web Development

The Vercel KV Cache Mystery: When Your Data Exists But Returns Null

How I discovered and fixed a sneaky type inference bug in Vercel KV that was breaking my Daily Melody app’s caching

The Problem: Cache Inconsistency

I was building the Daily Melody app, which I mentioned in the last post, using Vercel KV for caching. Everything looked good on paper: the data was being stored correctly, the cache keys existed, but my app was randomly fetching fresh data instead of using the cache.

The Symptom: My weather API was hitting the external weather service instead of using cached data, even though I could see the data existed in Vercel KV. To explain it better, here’s what was happening in my CacheService:

// This was returning null even when data existed
const cached = await kv.get<CachedWeatherData>(cacheKey);

The Debugging Process

I created two debug endpoints to understand what was going on. You can find the complete debug code in kv-simple Gist and kv-test Gist

Debug Endpoint 1: Direct KV Testing

I created /api/debug/kv-simple to test different KV retrieval methods:

// Test different ways to get the same key
const directValue = await kv.get(testKey);
const typedValue = await kv.get<any>(testKey);
const stringValue = await kv.get<string>(testKey);

Debug Endpoint 2: Cache Service Testing

I created /api/debug/kv-test to test my actual cache service:

// Test the full cache service workflow
const existingData = await cacheService.getWeatherData(testLocation, testTimestamp);
await cacheService.setWeatherData(testLocation, testWeather, new Date().toISOString(), testTimestamp);
const retrievedData = await cacheService.getWeatherData(testLocation, testTimestamp);

The Shocking Discovery

When I ran the debug endpoints, I found something that made no sense:

{
  "directValue": {
      "exists": false,
      "type": "object",
      "value": null
    },
    "typedValue": {
      "exists": true,
      "type": "object",
      "hasWeather": true,
      "weatherKeys": ["temperature", "condition", "humidity", "windSpeed", "description", "icon", "feelsLike", "pressure", "visibility", "lat", "lon"]
  }
}

The same key, same data, but completely different results depending on how I called kv.get()!

This revealed a clear pattern:

  • kv.get(cacheKey) → returns null
  • kv.get<any>(cacheKey) → returns the actual data
  • kv.get<CachedWeatherData>(cacheKey) → returns null

The Root Cause

This looked like a type inference bug in the KV client I was using in Vercel. When you use generics with kv.get<T>(), something goes wrong in the serialization/deserialization process.

My Theory

  1. KV stores data as JSON strings
  2. When you use kv.get<T>(), the client tries to deserialize with strict type checking.
  3. Something in the type inference logic fails silently and returns null
  4. When you use kv.get() without generics, it just returns the raw data
  5. Manual type casting works because you’re bypassing the problematic deserialization

The Fix

The solution was simple but not obvious. Instead of using generics:

// ❌ This was broken
cached = await kv.get<CachedWeatherData>(cacheKey);

I changed it to use type casting after the retrieval:

// ✅ This works
const rawCached = await kv.get(cacheKey);
cached = rawCached as CachedWeatherData | null;

Why this works: By removing the generic type parameter from kv.get(), we avoid the problematic type inference logic and get the raw data. Then we manually cast it to our expected type.

The Complete Fix in Context

Here’s the exact change I made in my CacheService:

// Before (broken)
async getWeatherData(locationKey: string, timestamp?: number): Promise<CachedWeatherData | null> {
  if (!this.isAvailable()) {
    return null;
  }

  try {
    const cacheKey = generateWeatherCacheKeyUTC(locationKey, timestamp);

    // ❌ This was the problem
    const cached = await kv.get<CachedWeatherData>(cacheKey);

    if (cached && cached.weather) {
      return cached;
    }
    return null;
  } catch (error) {
    console.warn('⚠️ KV cache error:', error);
    return null;
  }
}

// After (working)
async getWeatherData(locationKey: string, timestamp?: number): Promise<CachedWeatherData | null> {
  if (!this.isAvailable()) {
    return null;
  }

  try {
    const cacheKey = generateWeatherCacheKeyUTC(locationKey, timestamp);

    // ✅ This is the fix
    const rawCached = await kv.get(cacheKey);
    const cached = rawCached as CachedWeatherData | null;

    if (cached && cached.weather) {
      return cached;
    }
    return null;
  } catch (error) {
    console.warn('⚠️ KV cache error:', error);
    return null;
  }
}

The Result

After the fix, my cache service started working perfectly:

{
  "retrievedData": {
    "exists": true,
    "hasWeather": true,
    "weatherKeys": ["temperature", "condition", "humidity", "windSpeed", "description", "icon", "feelsLike", "pressure", "visibility", "lat", "lon"]
  }
}

Before the fix: Cache misses every time, always hitting external API

After the fix: Cache hits working perfectly, reducing API calls by ~90%

Final Thoughts

This was one of those bugs that makes you question your sanity. The data was there, the keys were correct, but the cache wouldn’t work.

The lesson? When dealing with external services like Vercel KV, always test the basic operations first. Don’t assume the obvious approach is the right one.

If you’re having similar issues with Vercel KV caching, try removing the generics and using type casting instead. It might just save you hours of debugging.


메타데이터
post_id
a6ae5d78f8cc
slug
the-vercel-kv-cache-mystery-when-your-data-exists-but-returns-null-a6ae5d78f8cc
url
https://medium.com/@darkaico/the-vercel-kv-cache-mystery-when-your-data-exists-but-returns-null-a6ae5d78f8cc
canonical_url
https://medium.com/@darkaico/the-vercel-kv-cache-mystery-when-your-data-exists-but-returns-null-a6ae5d78f8cc
author_url
https://medium.com/@darkaico
status
ok
fetched_at
2026-06-18 00:10:23