Skip to content
Back to skills

2077 Gotchas 60f1e757

ASecurity

**Cause:** Eventual consistency means writes may not be immediately visible in other regions **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 globally

  • 9 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added May 31, 2026
developmenttypescriptgoperformance

Security analysis

A100/100

Pro scans all 2 files and shows the line behind each finding

Scanned May 31, 2026

npx -y skills add tools-only/X-Skills --skill 2077-gotchas_60f1e757 --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of 2077 Gotchas 60f1e757?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for 2077 Gotchas 60f1e757
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/tools-only-2077-gotchas-60f1e757/badge)](https://www.skillsdirectory.com/skills/tools-only-2077-gotchas-60f1e757)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
# KV Gotchas & Troubleshooting

## Common Errors

### "Stale Read After Write"

**Cause:** Eventual consistency means writes may not be immediately visible in other regions  
**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 globally

```typescript
// ❌ BAD: Read immediately after write
await env.KV.put("key", "value");
const value = await env.KV.get("key"); // May be null in other regions!

// ✅ GOOD: Use the value you just wrote
const newValue = "value";
await env.KV.put("key", newValue);
return new Response(newValue); // Don't re-read
```

### "429 Rate Limit on Concurrent Writes"

**Cause:** Multiple concurrent writes to same key exceeding 1 write/second limit  
**Solution:** Use sequential writes, unique keys for concurrent operations, or implement retry with exponential backoff

```typescript
async function putWithRetry(
  kv: KVNamespace,
  key: string,
  value: string,
  maxAttempts = 5
): Promise<void> {
  let delay = 1000;
  for (let i = 0; i < maxAttempts; i++) {
    try {
      await kv.put(key, value);
      return;
    } catch (err) {
      if (err instanceof Error && err.message.includes("429")) {
        if (i === maxAttempts - 1) throw err;
        await new Promise(r => setTimeout(r, delay));
        delay *= 2; // Exponential backoff
      } else {
        throw err;
      }
    }
  }
}
```

### "Inefficient Multiple Gets"

**Cause:** Making multiple individual get() calls instead of bulk operation  
**Solution:** Use bulk get with array of keys: `env.USERS.get(["user:1", "user:2", "user:3"])` to reduce to 1 operation

### "Null Reference Error"

**Cause:** Attempting to use value without checking for null when key doesn't exist  
**Solution:** Always handle null returns - KV returns `null` for missing keys, not undefined

```typescript
// ❌ BAD: Assumes value exists
const config = await env.KV.get("config", "json");
return config.theme; // TypeError if null!

// ✅ GOOD: Null checks
const config = await env.KV.get("config", "json");
return config?.theme ?? "default";

// ✅ GOOD: Early return
const config = await env.KV.get("config", "json");
if (!config) return new Response("Not found", { status: 404 });
return new Response(config.theme);
```

### "Negative Lookup Caching"

**Cause:** Keys that don't exist are cached as "not found" for up to 60s  
**Solution:** Creating a key after checking won't be visible until cache expires

```typescript
// Check → create pattern has race condition
const exists = await env.KV.get("key"); // null, cached as "not found"
if (!exists) {
  await env.KV.put("key", "value");
  // Next get() may still return null for ~60s due to negative cache
}

// Alternative: Always assume key may not exist, use defaults
const value = await env.KV.get("key") ?? "default-value";
```

## Performance Tips

| Scenario | Recommendation | Why |
|----------|----------------|-----|
| Large values (>1MB) | Use `stream` type | Avoids buffering entire value in memory |
| Many small keys | Coalesce into one JSON object | Reduces operations, improves cache hit rate |
| High write volume | Spread across different keys | Avoid 1 write/second per-key limit |
| Cold reads | Increase `cacheTtl` parameter | Reduces latency for frequently-read data |
| Bulk operations | Use array form of get() | Single operation, better performance |

## Cost Examples

**Free tier:**
- 100K reads/day = 3M/month ✅
- 1K writes/day = 30K/month ✅
- 1GB storage ✅

**Example paid workload:**
- 10M reads/month = $5.00
- 100K writes/month = $0.50
- 1GB storage = $0.50
- **Total: ~$6/month**

## Limits

| Limit | Value | Notes |
|-------|-------|-------|
| Key size | 512 bytes | Maximum key length |
| Value size | 25 MiB | Maximum value; 413 error if exceeded |
| Metadata size | 1024 bytes | Maximum metadata per key |
| cacheTtl minimum | 60s | Minimum cache TTL |
| Write rate per key | 1 write/second | All plans; 429 error if exceeded |
| Propagation time | ≤60s | Global propagation time |
| Bulk get max | 100 keys | Maximum keys per bulk operation |
| Operations per Worker | 1,000 | Per request (bulk counts as 1) |
| Reads pricing | $0.50 per 10M | Per million reads |
| Writes pricing | $5.00 per 1M | Per million writes |
| Deletes pricing | $5.00 per 1M | Per million deletes |
| Storage pricing | $0.50 per GB-month | Per GB per month |

Files in this skill

  • README.md1.2 KB
  • skill.md4.4 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.