--- name: webflow-common-errors description: "Diagnose and fix Webflow Data API v2 errors \u2014 400, 401, 403, 404,\ \ 409, 429, 500.\nUse when encountering Webflow API errors, debugging failed requests,\n\ or troubleshooting integration issues.\nTrigger with phrases like \"webflow error\"\ , \"fix webflow\",\n\"webflow not working\", \"debug webflow\", \"webflow 429\"\ , \"webflow 401\".\n" allowed-tools: Read, Grep, Bash(curl:*), Bash(npx:*) version: 1.0.0 license: MIT author: Jeremy Longshore tags: - saas - design - no-code - webflow compatibility: Designed for Claude Code --- # Webflow Common Errors ## Overview Quick reference for the most common Webflow Data API v2 errors, their root causes, and concrete solutions. Covers every HTTP status code the API returns. ## Prerequisites - `webflow-api` SDK installed - API token configured - Access to application logs ## HTTP Error Reference ### 400 Bad Request — Invalid Input ``` { "code": "validation_error", "message": "Invalid field data" } ``` **Common causes:** - Missing required field (`name` and `slug` are always required for CMS items) - Wrong field type (sending string for a Number field) - Invalid slug format (must be lowercase, hyphens only, no spaces) - Bulk request exceeds 100 items **Fix:** ```typescript // Check collection schema before creating items const collection = await webflow.collections.get(collectionId); const requiredFields = collection.fields?.filter(f => f.isRequired); console.log("Required fields:", requiredFields?.map(f => `${f.slug} (${f.type})`)); // Validate slug format function isValidSlug(slug: string): boolean { return /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(slug); } ``` --- ### 401 Unauthorized — Invalid Token ``` { "code": "unauthorized", "message": "Not authorized" } ``` **Common causes:** - Token revoked or expired - Token copied with extra whitespace - Using v1 API key format with v2 endpoints **Fix:** ```bash # Verify token works curl -s https://api.webflow.com/v2/sites \ -H "Authorization: Bearer $WEBFLOW_API_TOKEN" \ -w "\nHTTP Status: %{http_code}\n" # Check for whitespace issues echo -n "$WEBFLOW_API_TOKEN" | wc -c ``` ```typescript // Programmatic token check async function verifyToken(): Promise { try { await webflow.sites.list(); return true; } catch (err: any) { if (err.statusCode === 401) { console.error("Token invalid. Generate new token at developers.webflow.com"); return false; } throw err; } } ``` --- ### 403 Forbidden — Missing Scope ``` { "code": "forbidden", "message": "Insufficient permissions" } ``` **Common causes:** - Token missing required scope (e.g., calling CMS write with only `cms:read`) - Site token used for a different site - OAuth app not authorized for the scope **Fix:** ```typescript // Required scopes by operation const SCOPE_MAP: Record = { "sites.list": "sites:read", "sites.publish": "sites:write", "collections.list": "cms:read", "collections.items.createItem": "cms:write", "pages.list": "pages:read", "forms.list": "forms:read", "products.list": "ecommerce:read", "products.create": "ecommerce:write", "orders.list": "ecommerce:read", "orders.refund": "ecommerce:write", }; ``` Generate a new token with the correct scopes at `https://developers.webflow.com`. --- ### 404 Not Found — Wrong Resource ID ``` { "code": "not_found", "message": "Resource not found" } ``` **Common causes:** - Wrong `site_id`, `collection_id`, or `item_id` - Resource deleted - Using staging ID against live endpoint (or vice versa) **Fix:** ```typescript // Discovery chain: always start from sites.list() async function discoverResources() { const { sites } = await webflow.sites.list(); console.log("Sites:", sites?.map(s => `${s.displayName}: ${s.id}`)); for (const site of sites!) { const { collections } = await webflow.collections.list(site.id!); console.log(` Collections in ${site.displayName}:`); for (const col of collections!) { console.log(` ${col.displayName}: ${col.id}`); } } } ``` --- ### 409 Conflict — Duplicate Resource ``` { "code": "conflict", "message": "Item with slug already exists" } ``` **Common causes:** - CMS item with same slug already exists in collection - Trying to create a resource that already exists **Fix:** ```typescript // Check for existing slug before creating async function createOrUpdate(collectionId: string, slug: string, fieldData: Record) { const { items } = await webflow.collections.items.listItems(collectionId); const existing = items?.find(i => i.fieldData?.slug === slug); if (existing) { return webflow.collections.items.updateItem(collectionId, existing.id!, { fieldData }); } return webflow.collections.items.createItem(collectionId, { fieldData: { slug, ...fieldData } }); } ``` --- ### 429 Too Many Requests — Rate Limited ``` HTTP/1.1 429 Too Many Requests Retry-After: 60 ``` **Common causes:** - Exceeded per-key rate limit - Site publish called more than once per minute - Rapid-fire requests without throttling **Fix:** ```typescript // The SDK auto-retries 429s with exponential backoff. // For manual control: async function waitForRateLimit(retryAfterSeconds: number) { console.log(`Rate limited. Waiting ${retryAfterSeconds}s...`); await new Promise(r => setTimeout(r, retryAfterSeconds * 1000)); } ``` See `webflow-rate-limits` for comprehensive rate limit handling. --- ### 500/502/503 — Webflow Server Error **Fix:** ```bash # 1. Check Webflow status curl -s https://status.webflow.com/api/v2/status.json | jq '.status' # 2. Retry with backoff (SDK handles this automatically) # 3. If persistent, check Webflow status page and open support ticket ``` ## Quick Diagnostic Commands ```bash # Test API connectivity curl -s -o /dev/null -w "%{http_code}" \ -H "Authorization: Bearer $WEBFLOW_API_TOKEN" \ https://api.webflow.com/v2/sites # Check rate limit headers curl -v -H "Authorization: Bearer $WEBFLOW_API_TOKEN" \ https://api.webflow.com/v2/sites 2>&1 | grep -i "x-ratelimit\|retry-after" # List sites (quick token verification) curl -s -H "Authorization: Bearer $WEBFLOW_API_TOKEN" \ https://api.webflow.com/v2/sites | jq '.sites[].displayName' # Check Webflow platform status curl -s https://status.webflow.com/api/v2/status.json | jq '.status.description' ``` ## Error Handling Pattern ```typescript import { WebflowClient } from "webflow-api"; async function resilientCall( operation: () => Promise, label: string ): Promise { try { return await operation(); } catch (err: any) { const status = err.statusCode || err.status; const actionMap: Record = { 400: "Fix request payload — check field names and types", 401: "Rotate token at developers.webflow.com", 403: "Add missing scope to token", 404: "Verify resource IDs with discovery chain", 409: "Handle duplicate — update instead of create", 429: "Wait for Retry-After header (SDK auto-retries)", 500: "Webflow server error — retry later", }; console.error(`[${label}] HTTP ${status}: ${actionMap[status] || "Unknown error"}`); console.error(` Message: ${err.message}`); console.error(` Body: ${JSON.stringify(err.body)}`); throw err; } } ``` ## Escalation Path 1. Check error code against this reference 2. Run diagnostic commands above 3. Collect evidence with `webflow-debug-bundle` 4. Check [Webflow Status](https://status.webflow.com) 5. Open support ticket with request ID and error details ## Output - Identified error cause from HTTP status code - Applied targeted fix - Verified resolution ## Resources - [Webflow API Error Codes](https://developers.webflow.com/data/reference/rest-introduction) - [Webflow Status Page](https://status.webflow.com) - [Rate Limits Reference](https://developers.webflow.com/data/reference/rate-limits) ## Next Steps For comprehensive debugging, see `webflow-debug-bundle`.