Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 08:24:36 +08:00
commit ca8c7fd975
15 changed files with 2860 additions and 0 deletions

645
references/common-issues.md Normal file
View File

@@ -0,0 +1,645 @@
# Common Issues and Troubleshooting
**Last Updated**: 2025-10-20
This document details all 6 documented issues that commonly affect Cloudflare Workers projects, with detailed explanations and fixes.
---
## Table of Contents
1. [Issue #1: Export Syntax Error](#issue-1-export-syntax-error)
2. [Issue #2: Static Assets Routing Conflicts](#issue-2-static-assets-routing-conflicts)
3. [Issue #3: Scheduled/Cron Not Exported](#issue-3-scheduledcron-not-exported)
4. [Issue #4: HMR Race Condition](#issue-4-hmr-race-condition)
5. [Issue #5: Static Assets Upload Race](#issue-5-static-assets-upload-race)
6. [Issue #6: Service Worker Format Confusion](#issue-6-service-worker-format-confusion)
---
## Issue #1: Export Syntax Error
### Symptoms
```
Error: Cannot read properties of undefined (reading 'map')
```
Deployment fails with TypeError during build or runtime.
### Source
- **GitHub Issue**: [honojs/hono #3955](https://github.com/honojs/hono/issues/3955)
- **Related**: [honojs/vite-plugins #237](https://github.com/honojs/vite-plugins/issues/237)
- **Reported**: February 2025
### Root Cause
When using Hono with Vite's build tools, the incorrect export pattern breaks the `this` context:
```typescript
// ❌ WRONG: This causes the error
export default {
fetch: app.fetch
}
```
**Why it breaks:**
- Vite's bundler transforms the code
- The `app.fetch` binding loses its `this` context
- When Cloudflare calls `fetch()`, `this` is `undefined`
- Hono tries to access `this.routes.map(...)` → Error
### Fix
Use the direct export pattern:
```typescript
import { Hono } from 'hono'
const app = new Hono()
// Define routes...
// ✅ CORRECT
export default app
```
**Why this works:**
- Hono's app object already implements the fetch handler
- No context binding is lost
- Vite can properly bundle the code
### Exception: When You Need Multiple Handlers
If you need scheduled/tail handlers, use Module Worker format:
```typescript
export default {
fetch: app.fetch,
scheduled: async (event, env, ctx) => {
console.log('Cron triggered:', event.cron)
}
}
```
This works because Cloudflare's runtime handles the binding correctly for Module Workers.
### How to Verify Fix
1. Check your `src/index.ts` export
2. Ensure it's `export default app`
3. Run `npm run dev` → Should start without errors
4. Run `npm run deploy` → Should deploy successfully
5. Test API endpoints → Should return JSON (not errors)
---
## Issue #2: Static Assets Routing Conflicts
### Symptoms
- API routes return `index.html` instead of JSON
- API endpoints return status 200 but wrong content-type (text/html instead of application/json)
- Browser console shows HTML when expecting JSON
### Example
```bash
curl http://localhost:8787/api/hello
# Expected: {"message":"Hello"}
# Actual: <!DOCTYPE html><html>...
```
### Source
- **GitHub Issue**: [workers-sdk #8879](https://github.com/cloudflare/workers-sdk/issues/8879)
- **Reported**: April 2025
### Root Cause
The `not_found_handling: "single-page-application"` configuration creates a fallback:
```
Request → File not found → Return index.html
```
**Without `run_worker_first`:**
1. Request to `/api/hello`
2. Static Assets handler checks: "Does `/api/hello` file exist?"
3. No → SPA fallback → Returns `public/index.html`
4. Your Worker never runs!
### Fix
Add `run_worker_first` to `wrangler.jsonc`:
```jsonc
{
"assets": {
"directory": "./public/",
"binding": "ASSETS",
"not_found_handling": "single-page-application",
"run_worker_first": ["/api/*"] // ← CRITICAL
}
}
```
**What this does:**
- Requests matching `/api/*` go to your Worker FIRST
- If Worker doesn't handle it, then try Static Assets
- Ensures API routes are never intercepted by SPA fallback
### Advanced Configuration
```jsonc
{
"assets": {
"run_worker_first": [
"/api/*",
"/auth/*",
"/webhooks/*",
"/_app/*"
]
}
}
```
### How to Verify Fix
1. Start dev server: `npm run dev`
2. Test API endpoint:
```bash
curl -i http://localhost:8787/api/hello
```
3. Check response:
- ✅ `Content-Type: application/json`
- ✅ JSON body
4. Test static file:
```bash
curl -i http://localhost:8787/
```
5. Check response:
- ✅ `Content-Type: text/html`
- ✅ HTML body
---
## Issue #3: Scheduled/Cron Not Exported
### Symptoms
```
Error: Handler does not export a scheduled() function
```
Deployment succeeds, but cron triggers fail.
### Source
- **GitHub Issue**: [honojs/vite-plugins #275](https://github.com/honojs/vite-plugins/issues/275)
- **Reported**: July 2025
### Root Cause
The `@hono/vite-build/cloudflare-workers` plugin **only supports the `fetch` handler**.
If you use:
```typescript
export default app // Only exports fetch handler
```
...then scheduled/tail handlers are not exported.
### Fix Option 1: Use Module Worker Format
```typescript
import { Hono } from 'hono'
const app = new Hono()
// Define routes...
// ✅ Export multiple handlers
export default {
fetch: app.fetch,
scheduled: async (event, env, ctx) => {
console.log('Cron triggered:', event.cron)
// Your scheduled logic here
},
tail: async (events, env, ctx) => {
// Tail handler logic
console.log('Tail events:', events)
}
}
```
### Fix Option 2: Use @cloudflare/vite-plugin
Instead of `@hono/vite-build/cloudflare-workers`, use the official Cloudflare plugin:
```bash
npm uninstall @hono/vite-build
npm install -D @cloudflare/vite-plugin
```
Update `vite.config.ts`:
```typescript
import { defineConfig } from 'vite'
import { cloudflare } from '@cloudflare/vite-plugin'
export default defineConfig({
plugins: [cloudflare()],
})
```
This plugin supports all handler types.
### Configure Cron in wrangler.jsonc
```jsonc
{
"triggers": {
"crons": ["0 0 * * *"] // Daily at midnight UTC
}
}
```
### How to Verify Fix
1. Deploy: `npm run deploy`
2. Trigger manually:
```bash
wrangler deploy && wrangler tail
```
3. Wait for cron or trigger via dashboard
4. Check logs for scheduled handler output
---
## Issue #4: HMR Race Condition
### Symptoms
```
Error: A hanging Promise was canceled
```
- Development server crashes during file changes
- Happens with rapid HMR updates
- Requires manual restart
### Source
- **GitHub Issue**: [workers-sdk #9518](https://github.com/cloudflare/workers-sdk/issues/9518)
- **Related**: [workers-sdk #9249](https://github.com/cloudflare/workers-sdk/issues/9249)
- **Reported**: June 2025
### Root Cause
**Race condition in `@cloudflare/vite-plugin` versions 1.1.1 through 1.11.x:**
1. File change detected
2. Vite triggers HMR
3. Plugin cancels old Worker instance
4. New instance starts before old one fully terminates
5. Promise cancellation error thrown
### Fix
Update to latest `@cloudflare/vite-plugin`:
```bash
npm install -D @cloudflare/vite-plugin@1.13.13
```
**Fixed in version 1.13.13** (October 2025)
### Alternative: Configure Vite with Persistence
If updating doesn't fix it, try:
```typescript
// vite.config.ts
import { defineConfig } from 'vite'
import { cloudflare } from '@cloudflare/vite-plugin'
export default defineConfig({
plugins: [
cloudflare({
persist: true, // Persist state between HMR updates
}),
],
})
```
### How to Verify Fix
1. Start dev server: `npm run dev`
2. Make rapid file changes (edit `src/index.ts` 5 times quickly)
3. Check terminal:
- ✅ No "hanging Promise" errors
- ✅ HMR updates smoothly
4. Test API endpoint after each change:
```bash
curl http://localhost:8787/api/hello
```
---
## Issue #5: Static Assets Upload Race
### Symptoms
- Deployment fails **non-deterministically** in CI/CD
- Works locally, fails in CI randomly
- Error messages vary:
- "Failed to upload assets"
- "Timeout during asset upload"
- "Asset manifest mismatch"
### Source
- **GitHub Issue**: [workers-sdk #7555](https://github.com/cloudflare/workers-sdk/issues/7555)
- **Reported**: March 2025
### Root Cause
**Race condition during parallel asset uploads:**
1. Wrangler uploads multiple assets simultaneously
2. Cloudflare's asset store processes uploads
3. Manifest is generated before all uploads complete
4. Deployment validation fails
**Most common in CI/CD** because:
- Network latency varies
- Parallel execution timing is different
- No user interaction to retry
### Fix Option 1: Use Wrangler 4.x+ (Recommended)
Wrangler 4.x includes improved upload logic:
```bash
npm install -D wrangler@latest
```
**Improvements in 4.x:**
- Sequential upload of critical assets
- Better retry logic
- Manifest generation after all uploads complete
### Fix Option 2: Add Retry Logic to CI/CD
```yaml
# GitHub Actions example
- name: Deploy to Cloudflare
run: |
for i in {1..3}; do
npm run deploy && break || sleep 10
done
```
```bash
# Shell script
#!/bin/bash
for i in {1..3}; do
npm run deploy && break || sleep 10
done
```
### Fix Option 3: Reduce Asset Count
If you have many small files, bundle them:
```typescript
// vite.config.ts
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-dom'],
},
},
},
},
})
```
### How to Verify Fix
1. Deploy locally 5 times:
```bash
for i in {1..5}; do npm run deploy; done
```
2. All deployments should succeed
3. Run in CI/CD pipeline
4. Check logs for upload errors
---
## Issue #6: Service Worker Format Confusion
### Symptoms
- Using deprecated `addEventListener('fetch', ...)` pattern
- TypeScript errors about missing types
- Bindings don't work (KV, D1, R2)
- Modern Cloudflare features unavailable
### Source
- **Cloudflare Migration Guide**: https://developers.cloudflare.com/workers/configuration/compatibility-dates/
- **Multiple Stack Overflow questions** (2024-2025)
### Root Cause
**Old tutorials and templates** still use the deprecated Service Worker format:
```typescript
// ❌ DEPRECATED: Service Worker format
addEventListener('fetch', (event) => {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
return new Response('Hello World')
}
```
**Problems with this format:**
- Doesn't support bindings (KV, D1, R2, etc.)
- No TypeScript types
- No environment variable access
- Deprecated since Workers v2 (2021)
### Fix: Use ES Module Format
```typescript
// ✅ CORRECT: ES Module format
export default {
fetch(request, env, ctx) {
return new Response('Hello World')
}
}
```
**With Hono:**
```typescript
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => c.text('Hello World'))
export default app
```
### Migration Steps
1. **Remove `addEventListener`**:
```diff
- addEventListener('fetch', (event) => {
- event.respondWith(handleRequest(event.request))
- })
```
2. **Change to ES Module export**:
```diff
+ export default {
+ fetch(request, env, ctx) {
+ return handleRequest(request, env)
+ }
+ }
```
3. **Update function signatures** to accept `env`:
```diff
- async function handleRequest(request) {
+ async function handleRequest(request, env) {
// Now you can access env.MY_KV, env.DB, etc.
```
4. **Update `wrangler.toml` → `wrangler.jsonc`**:
```bash
# Convert TOML to JSONC (preferred since Wrangler v3.91.0)
```
### How to Verify Fix
1. Check `src/index.ts`:
- ✅ No `addEventListener`
- ✅ Has `export default`
2. Check you can access bindings:
```typescript
const value = await env.MY_KV.get('key')
```
3. TypeScript types work:
```typescript
type Bindings = {
MY_KV: KVNamespace
}
```
---
## General Troubleshooting Tips
### Check Package Versions
```bash
npm list hono @cloudflare/vite-plugin wrangler
```
**Expected (as of 2025-10-20):**
- `hono@4.10.1`
- `@cloudflare/vite-plugin@1.13.13`
- `wrangler@4.43.0`
### Clear Wrangler Cache
```bash
rm -rf node_modules/.wrangler
rm -rf .wrangler
npm run dev
```
### Check Wrangler Config
```bash
wrangler whoami # Verify authentication
wrangler dev --local # Test without deploying
```
### Enable Verbose Logging
```bash
WRANGLER_LOG=debug npm run dev
WRANGLER_LOG=debug npm run deploy
```
### Check Browser Console
Many issues are visible in the browser:
- Open DevTools → Network tab
- Check response Content-Type
- Check response body
- Look for CORS errors
### Test with curl
```bash
# Test API endpoint
curl -i http://localhost:8787/api/hello
# Test POST
curl -X POST http://localhost:8787/api/echo \
-H "Content-Type: application/json" \
-d '{"test":"data"}'
# Test static file
curl -i http://localhost:8787/styles.css
```
---
## Issue Summary Table
| Issue | Error Message | Source | Fix |
|-------|---------------|--------|-----|
| **#1** | "Cannot read properties of undefined" | hono #3955 | `export default app` |
| **#2** | API routes return HTML | workers-sdk #8879 | `run_worker_first: ["/api/*"]` |
| **#3** | "Handler does not export scheduled()" | vite-plugins #275 | Module Worker format or @cloudflare/vite-plugin |
| **#4** | "A hanging Promise was canceled" | workers-sdk #9518 | Update to vite-plugin@1.13.13+ |
| **#5** | Non-deterministic deployment failures | workers-sdk #7555 | Use Wrangler 4.x+ with retry |
| **#6** | Service Worker format issues | Cloudflare migration | Use ES Module format |
---
## Getting Help
If you encounter issues not covered here:
1. **Check official docs**:
- Cloudflare Workers: https://developers.cloudflare.com/workers/
- Hono: https://hono.dev/
2. **Search GitHub issues**:
- workers-sdk: https://github.com/cloudflare/workers-sdk/issues
- hono: https://github.com/honojs/hono/issues
3. **Ask in Discord**:
- Cloudflare Developers: https://discord.gg/cloudflaredev
- Hono: https://discord.gg/hono
4. **Check Stack Overflow**:
- Tag: `cloudflare-workers`
---
**All issues documented with GitHub sources**
**All fixes production-tested**