Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Comprehensive Cloudflare platform skill covering Workers, D1, R2, KV, AI, Durable Objects, and security.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/kv/gotchas.md
1# KV Gotchas & Troubleshooting23## Common Errors45### "Stale Read After Write"67**Cause:** Eventual consistency means writes may not be immediately visible in other regions8**Solution:** Don't read immediately after write; return confirmation without reading or use the local value you just wrote. Writes visible immediately in same location, ≤60s globally910```typescript11// ❌ BAD: Read immediately after write12await env.KV.put("key", "value");13const value = await env.KV.get("key"); // May be null in other regions!1415// ✅ GOOD: Use the value you just wrote16const newValue = "value";17await env.KV.put("key", newValue);18return new Response(newValue); // Don't re-read19```2021### "429 Rate Limit on Concurrent Writes"2223**Cause:** Multiple concurrent writes to same key exceeding 1 write/second limit24**Solution:** Use sequential writes, unique keys for concurrent operations, or implement retry with exponential backoff2526```typescript27async function putWithRetry(28kv: KVNamespace,29key: string,30value: string,31maxAttempts = 532): Promise<void> {33let delay = 1000;34for (let i = 0; i < maxAttempts; i++) {35try {36await kv.put(key, value);37return;38} catch (err) {39if (err instanceof Error && err.message.includes("429")) {40if (i === maxAttempts - 1) throw err;41await new Promise(r => setTimeout(r, delay));42delay *= 2; // Exponential backoff43} else {44throw err;45}46}47}48}49```5051### "Inefficient Multiple Gets"5253**Cause:** Making multiple individual get() calls instead of bulk operation54**Solution:** Use bulk get with array of keys: `env.USERS.get(["user:1", "user:2", "user:3"])` to reduce to 1 operation5556### "Null Reference Error"5758**Cause:** Attempting to use value without checking for null when key doesn't exist59**Solution:** Always handle null returns - KV returns `null` for missing keys, not undefined6061```typescript62// ❌ BAD: Assumes value exists63const config = await env.KV.get("config", "json");64return config.theme; // TypeError if null!6566// ✅ GOOD: Null checks67const config = await env.KV.get("config", "json");68return config?.theme ?? "default";6970// ✅ GOOD: Early return71const config = await env.KV.get("config", "json");72if (!config) return new Response("Not found", { status: 404 });73return new Response(config.theme);74```7576### "Negative Lookup Caching"7778**Cause:** Keys that don't exist are cached as "not found" for up to 60s79**Solution:** Creating a key after checking won't be visible until cache expires8081```typescript82// Check → create pattern has race condition83const exists = await env.KV.get("key"); // null, cached as "not found"84if (!exists) {85await env.KV.put("key", "value");86// Next get() may still return null for ~60s due to negative cache87}8889// Alternative: Always assume key may not exist, use defaults90const value = await env.KV.get("key") ?? "default-value";91```9293## Performance Tips9495| Scenario | Recommendation | Why |96|----------|----------------|-----|97| Large values (>1MB) | Use `stream` type | Avoids buffering entire value in memory |98| Many small keys | Coalesce into one JSON object | Reduces operations, improves cache hit rate |99| High write volume | Spread across different keys | Avoid 1 write/second per-key limit |100| Cold reads | Increase `cacheTtl` parameter | Reduces latency for frequently-read data |101| Bulk operations | Use array form of get() | Single operation, better performance |102103## Cost Examples104105**Free tier:**106- 100K reads/day = 3M/month ✅107- 1K writes/day = 30K/month ✅108- 1GB storage ✅109110**Example paid workload:**111- 10M reads/month = $5.00112- 100K writes/month = $0.50113- 1GB storage = $0.50114- **Total: ~$6/month**115116## Limits117118| Limit | Value | Notes |119|-------|-------|-------|120| Key size | 512 bytes | Maximum key length |121| Value size | 25 MiB | Maximum value; 413 error if exceeded |122| Metadata size | 1024 bytes | Maximum metadata per key |123| cacheTtl minimum | 60s | Minimum cache TTL |124| Write rate per key | 1 write/second | All plans; 429 error if exceeded |125| Propagation time | ≤60s | Global propagation time |126| Bulk get max | 100 keys | Maximum keys per bulk operation |127| Operations per Worker | 1,000 | Per request (bulk counts as 1) |128| Reads pricing | $0.50 per 1M | Per million reads |129| Writes pricing | $5.00 per 1M | Per million writes |130| Deletes pricing | $5.00 per 1M | Per million deletes |131| Storage pricing | $0.50 per GB-month | Per GB per month |132