# Gotchas & Troubleshooting

## Rate Limits & 429 Errors

**Actual Limits:**

- **1200 requests / 5 minutes** per user/token (global)
- **200 requests / second** per IP address
- **GraphQL: 320 / 5 minutes** (cost-based)

**SDK Behavior:**

- Auto-retry with exponential backoff (default 2 retries, Go: 10)
- Respects `Retry-After` header
- Throws `RateLimitError` after exhausting retries

**Solution:**

```typescript
// Increase retries for rate-limit-heavy workflows
const client = new Cloudflare({ maxRetries: 5 })

// Add application-level throttling
import pLimit from 'p-limit'
const limit = pLimit(10) // Max 10 concurrent requests
```

## SDK-Specific Issues

### Go: Required Field Wrapper

**Problem:** Go SDK requires `cloudflare.F()` wrapper for optional fields.

```go
// ❌ WRONG - Won't compile or send field
client.Zones.New(ctx, cloudflare.ZoneNewParams{
    Name: "example.com",
})

// ✅ CORRECT
client.Zones.New(ctx, cloudflare.ZoneNewParams{
    Name: cloudflare.F("example.com"),
    Account: cloudflare.F(cloudflare.ZoneNewParamsAccount{
        ID: cloudflare.F("account-id"),
    }),
})
```

**Why:** Distinguishes between zero value, null, and omitted fields.

### Python: Async vs Sync Clients

**Problem:** Using sync client in async context or vice versa.

```python
# ❌ WRONG - Can't await sync client
from cloudflare import Cloudflare
client = Cloudflare()
await client.zones.list()  # TypeError

# ✅ CORRECT - Use AsyncCloudflare
from cloudflare import AsyncCloudflare
client = AsyncCloudflare()
await client.zones.list()
```

## Token Permission Errors (403)

**Problem:** API returns 403 Forbidden despite valid token.

**Cause:** Token lacks required permissions (scope).

**Scopes Required:**

| Operation     | Required Scope                          |
| ------------- | --------------------------------------- |
| List zones    | Zone:Read (zone-level or account-level) |
| Create zone   | Zone:Edit (account-level)               |
| Edit DNS      | DNS:Edit (zone-level)                   |
| Deploy Worker | Workers Script:Edit (account-level)     |
| Read KV       | Workers KV Storage:Read                 |
| Write KV      | Workers KV Storage:Edit                 |

**Solution:** Re-create token with correct permissions in Dashboard → My Profile → API Tokens.

## Pagination Truncation

**Problem:** Only getting first 20 results (default page size).

**Solution:** Use auto-pagination iterators.

```typescript
// ❌ WRONG - Only first page (20 items)
const page = await client.zones.list()

// ✅ CORRECT - All results
const zones = []
for await (const zone of client.zones.list()) {
  zones.push(zone)
}
```

## Workers Subrequests

**Problem:** Rate limit hit faster than expected in Workers.

**Cause:** Workers subrequests count as separate API calls.

**Solution:** Use bindings instead of REST API in Workers (see ../bindings/).

```typescript
// ❌ WRONG - REST API in Workers (counts against rate limit)
const client = new Cloudflare({ apiToken: env.CLOUDFLARE_API_TOKEN })
const zones = await client.zones.list()

// ✅ CORRECT - Use bindings (no rate limit)
// Access via env.MY_BINDING
```

## Authentication Errors (401)

**Problem:** "Authentication failed" or "Invalid token"

**Causes:**

- Token expired
- Token deleted/revoked
- Token not set in environment
- Wrong token format

**Solution:**

```typescript
// Verify token is set
if (!process.env.CLOUDFLARE_API_TOKEN) {
  throw new Error('CLOUDFLARE_API_TOKEN not set')
}

// Test token
const user = await client.user.tokens.verify()
console.log('Token valid:', user.status)
```

## Timeout Errors

**Problem:** Request times out (default 60s).

**Cause:** Large operations (bulk DNS, zone transfers).

**Solution:** Increase timeout or split operations.

```typescript
// Increase timeout
const client = new Cloudflare({
  timeout: 300000, // 5 minutes
})

// Or split operations
const batchSize = 100
for (let i = 0; i < records.length; i += batchSize) {
  const batch = records.slice(i, i + batchSize)
  await processBatch(batch)
}
```

## Zone Not Found (404)

**Problem:** Zone ID valid but returns 404.

**Causes:**

- Zone not in account associated with token
- Zone deleted
- Wrong zone ID format

**Solution:**

```typescript
// List all zones to find correct ID
for await (const zone of client.zones.list()) {
  console.log(zone.id, zone.name)
}
```

## Limits Reference

| Resource/Limit                  | Value     | Notes                  |
| ------------------------------- | --------- | ---------------------- |
| API rate limit                  | 1200/5min | Per user/token         |
| IP rate limit                   | 200/sec   | Per IP                 |
| GraphQL rate limit              | 320/5min  | Cost-based             |
| Parallel requests (recommended) | < 10      | Avoid overwhelming API |
| Default page size               | 20        | Use auto-pagination    |
| Max page size                   | 50        | Some endpoints         |

## Best Practices

**Security:**

- Never commit tokens
- Use minimal permissions
- Rotate tokens regularly
- Set token expiration

**Performance:**

- Batch operations
- Use pagination wisely
- Cache responses
- Handle rate limits

**Code Organization:**

```typescript
// Create reusable client instance
export const cfClient = new Cloudflare({
  apiToken: process.env.CLOUDFLARE_API_TOKEN,
  maxRetries: 5,
})

// Wrap common operations
export async function getZoneDetails(zoneId: string) {
  return await cfClient.zones.get({ zone_id: zoneId })
}
```

## See Also

- [api.md](./api.md) - Error types, authentication
- [configuration.md](./configuration.md) - Timeout/retry configuration
- [patterns.md](./patterns.md) - Error handling patterns
