| name | debug-fetch |
| description | Debug broken API calls and fetch failures in CrossTide. Use when: a card shows an error state, data is not loading or stale, worker route returns 502, provider chain failing, or fetch never resolves. Covers worker tail logs, KV state inspection, fixture comparison, and common failure patterns. |
| argument-hint | Which route or card is broken? (e.g. /api/quote/AAPL, screener-card) |
🐛 Debug Fetch — CrossTide
Use this skill when the problem is in transport, provider chain, worker validation, KV cache state, or response envelope shape.
1️⃣ Step 1 — Worker Tail
cd worker
./node_modules/.bin/wrangler tail
Watch for:
- Status code (200 / 4xx / 5xx)
- Validation errors from Valibot
- Provider upstream errors
- Rate-limit hits
2️⃣ Step 2 — Identify the Layer
| Symptom | Likely Cause |
|---|
| Worker returns 502 | Upstream provider error or schema mismatch |
| Worker returns 400 | Client request validation failed |
| Worker returns 429 | Rate limit exceeded |
| Worker returns demo / fixture data | KV binding missing — check worker/wrangler.toml |
| Card shows stale data forever | TTL too long or unmount() not clearing timer |
| All routes red after deploy | wrangler deploy --dry-run schema error |
| OpenAPI client mismatch | worker/routes/openapi.ts drift — regenerate |
3️⃣ Step 3 — Check Bindings
Get-Content worker/wrangler.toml
Verify NO line still says PLACEHOLDER_KV_NAMESPACE_ID or PLACEHOLDER_D1_DATABASE_ID. Run the provisioning script if needed.
4️⃣ Step 4 — Test Route Directly
cd worker
./node_modules/.bin/wrangler dev --local
In another terminal:
curl "http://localhost:8787/api/quote/AAPL"
curl "http://localhost:8787/api/health"
Expected envelope:
{ "data": { ... }, "source": "yahoo|cache|demo", "ts": 1730000000000 }
5️⃣ Step 5 — Inspect Browser-Side Cache
Object.keys(localStorage).filter((k) => k.startsWith("crosstide_"));
JSON.parse(localStorage.getItem("crosstide_quote_AAPL"));
Or for the IDB-backed signal stores:
indexedDB.open("crosstide");
Delete the key + reload to force re-fetch.
6️⃣ Step 6 — Schema Drift
If a 502 mentions Valibot:
- Open
worker/providers/<provider>.ts
- Find the schema near the failing field
- Add
v.passthrough() to the parent object
- Redeploy and call the route — log the raw response
- Tighten the schema with the new field shape
- Update
tests/unit/worker/<route>.test.ts fixture
- Remove
v.passthrough() again
7️⃣ Step 7 — Run Targeted Tests
./node_modules/.bin/vitest run tests/unit/worker/<route>.test.ts --reporter=verbose
If tests pass but production fails, the difference is real upstream — capture the response body via wrangler tail and update the fixture.
8️⃣ Step 8 — Common Fixes
| Problem | Fix |
|---|
502 with validation in body | Schema too strict — add field, retry, tighten |
| Always returns demo data | env.QUOTE_CACHE not bound — fix worker/wrangler.toml ID |
| 429 on every request | Rate-limit budget too low — tune worker/rate-limit.ts |
| Client fails CORS preflight | Add origin to allowlist in worker/cors.ts |
| Stale cache served past TTL | KV TTL not honoured — check kvPut expirationTtl |
| OpenAPI schema drift | Run npm run gen:api-types; update worker/routes/openapi.ts |
| Card never updates | unmount() missing — timer leak, multiple loaders racing |
9️⃣ Step 9 — Commit
When the fix is in:
fix(worker): tighten <provider> schema for <field> drift
fix(cards): clear refresh timer on unmount in <name>-card