# Binding Gotchas and Troubleshooting

## Critical: Global Scope Mutation

### ❌ THE #1 GOTCHA: Caching env in Global Scope

```typescript
// ❌ DANGEROUS - env cached at deploy time
const apiKey = env.API_KEY // ERROR: env not available in global scope

export default {
  async fetch(request: Request, env: Env) {
    // Uses undefined or stale value!
  },
}
```

**Why it breaks:**

- `env` not available in global scope
- If using workarounds, secrets may not update without redeployment
- Leads to "Cannot read property 'X' of undefined" errors

**✅ Always access env per-request:**

```typescript
export default {
  async fetch(request: Request, env: Env) {
    const apiKey = env.API_KEY // Fresh every request
  },
}
```

## Common Errors

### "env.MY_KV is undefined"

**Cause:** Name mismatch or not configured  
**Solution:** Check wrangler.jsonc (case-sensitive), run `npx wrangler types`, verify `npx wrangler kv namespace list`

### "Property 'MY_KV' does not exist on type 'Env'"

**Cause:** Types not generated  
**Solution:** `npx wrangler types`

### "preview_id is required for --remote"

**Cause:** Missing preview binding  
**Solution:** Add `"preview_id": "dev-id"` or use `npx wrangler dev` (local mode)

### "Secret updated but Worker still uses old value"

**Cause:** Cached in global scope or not redeployed  
**Solution:** Avoid global caching, redeploy after secret change

### "KV get() returns null for existing key"

**Cause:** Eventual consistency (60s), wrong namespace, wrong environment  
**Solution:**

```bash
# Check key exists
npx wrangler kv key get --binding=MY_KV "your-key"

# Verify namespace ID
npx wrangler kv namespace list

# Check environment
npx wrangler deployments list
```

### "D1 database not found"

**Solution:** `npx wrangler d1 list`, verify ID in wrangler.jsonc

### "Service binding returns 'No such service'"

**Cause:** Target Worker not deployed, name mismatch, environment mismatch  
**Solution:**

```bash
# List deployed Workers
npx wrangler deployments list --name=target-worker

# Check service binding config
cat wrangler.jsonc | grep -A2 services

# Deploy target first
cd ../target-worker && npx wrangler deploy
```

### "Rate limit exceeded" on KV writes

**Cause:** >1 write/second per key  
**Solution:** Use different keys, Durable Objects, or Queues

## Type Safety Gotchas

### Missing @cloudflare/workers-types

**Error:** `Cannot find name 'Request'`  
**Solution:** `npm install -D @cloudflare/workers-types`, add to tsconfig.json `"types"`

### Binding Type Mismatches

```typescript
// ❌ Wrong - KV returns string | null
const value: string = await env.MY_KV.get('key')

// ✅ Handle null
const value = await env.MY_KV.get('key')
if (!value) return new Response('Not found', { status: 404 })
```

## Environment Gotchas

### Wrong Environment Deployed

**Solution:** Check `npx wrangler deployments list`, use `--env` flag

### Secrets Not Per-Environment

**Solution:** Set per environment: `npx wrangler secret put API_KEY --env staging`

## Development Gotchas

**wrangler dev vs deploy:**

- dev: Uses `preview_id` or local bindings, secrets not available
- deploy: Uses production `id`, secrets available

**Access secrets in dev:** `npx wrangler dev --remote`  
**Persist local data:** `npx wrangler dev --persist`

## Performance Gotchas

### Sequential Binding Calls

```typescript
// ❌ Slow
const user = await env.DB.prepare('...').first()
const config = await env.MY_KV.get('config')

// ✅ Parallel
const [user, config] = await Promise.all([env.DB.prepare('...').first(), env.MY_KV.get('config')])
```

## Security Gotchas

**❌ Secrets in logs:** `console.log('Key:', env.API_KEY)` - visible in dashboard  
**✅** `console.log('Key:', env.API_KEY ? '***' : 'missing')`

**❌ Exposing env:** `return Response.json(env)` - exposes all bindings  
**✅** Never return env object in responses

## Limits Reference

| Resource                  | Limit                  | Impact                         | Plan |
| ------------------------- | ---------------------- | ------------------------------ | ---- |
| **Bindings per Worker**   | 64 total               | All binding types combined     | All  |
| **Environment variables** | 64 max, 5KB each       | Per Worker                     | All  |
| **Secret size**           | 1KB                    | Per secret                     | All  |
| **KV key size**           | 512 bytes              | UTF-8 encoded                  | All  |
| **KV value size**         | 25 MB                  | Per value                      | All  |
| **KV writes per key**     | 1/second               | Per key; exceeding = 429 error | All  |
| **KV list() results**     | 1000 keys              | Per call; use cursor for more  | All  |
| **KV operations**         | 1000 reads/day         | Free tier only                 | Free |
| **R2 object size**        | 5 TB                   | Per object                     | All  |
| **R2 operations**         | 1M Class A/month free  | Writes                         | All  |
| **D1 database size**      | 10 GB                  | Per database                   | All  |
| **D1 rows per query**     | 100,000                | Result set limit               | All  |
| **D1 databases**          | 10                     | Free tier                      | Free |
| **Queue batch size**      | 100 messages           | Per consumer batch             | All  |
| **Queue message size**    | 128 KB                 | Per message                    | All  |
| **Service binding calls** | Unlimited              | Counts toward CPU time         | All  |
| **Durable Objects**       | 1M requests/month free | First 1M                       | Free |

## Debugging Tips

```bash
# Check configuration
npx wrangler deploy --dry-run       # Validate config without deploying
npx wrangler kv namespace list      # List KV namespaces
npx wrangler secret list            # List secrets (not values)
npx wrangler deployments list       # Recent deployments

# Inspect bindings
npx wrangler kv key list --binding=MY_KV
npx wrangler kv key get --binding=MY_KV "key-name"
npx wrangler r2 object get my-bucket/file.txt
npx wrangler d1 execute my-db --command="SELECT * FROM sqlite_master"

# Test locally
npx wrangler dev                  # Local mode
npx wrangler dev --remote         # Production bindings
npx wrangler dev --persist        # Persist data across restarts

# Verify types
npx wrangler types
cat .wrangler/types/runtime.d.ts | grep "interface Env"

# Debug specific binding issues
npx wrangler tail                 # Stream logs in real-time
npx wrangler tail --format=pretty # Formatted logs
```

## See Also

- [Workers Limits](https://developers.cloudflare.com/workers/platform/limits/)
- [Wrangler Commands](https://developers.cloudflare.com/workers/wrangler/commands/)
