Files
gh-jezweb-claude-skills-ski…/references/common-issues.md
2025-11-30 08:24:36 +08:00

14 KiB

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
  2. Issue #2: Static Assets Routing Conflicts
  3. Issue #3: Scheduled/Cron Not Exported
  4. Issue #4: HMR Race Condition
  5. Issue #5: Static Assets Upload Race
  6. 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

Root Cause

When using Hono with Vite's build tools, the incorrect export pattern breaks the this context:

// ❌ 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:

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:

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

curl http://localhost:8787/api/hello
# Expected: {"message":"Hello"}
# Actual: <!DOCTYPE html><html>...

Source

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:

{
  "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

{
  "assets": {
    "run_worker_first": [
      "/api/*",
      "/auth/*",
      "/webhooks/*",
      "/_app/*"
    ]
  }
}

How to Verify Fix

  1. Start dev server: npm run dev
  2. Test API endpoint:
    curl -i http://localhost:8787/api/hello
    
  3. Check response:
    • Content-Type: application/json
    • JSON body
  4. Test static file:
    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

Root Cause

The @hono/vite-build/cloudflare-workers plugin only supports the fetch handler.

If you use:

export default app  // Only exports fetch handler

...then scheduled/tail handlers are not exported.

Fix Option 1: Use Module Worker Format

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:

npm uninstall @hono/vite-build
npm install -D @cloudflare/vite-plugin

Update vite.config.ts:

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

{
  "triggers": {
    "crons": ["0 0 * * *"]  // Daily at midnight UTC
  }
}

How to Verify Fix

  1. Deploy: npm run deploy
  2. Trigger manually:
    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

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:

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:

// 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:
    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

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

Wrangler 4.x includes improved upload logic:

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

# GitHub Actions example
- name: Deploy to Cloudflare
  run: |
    for i in {1..3}; do
      npm run deploy && break || sleep 10
    done
# 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:

// vite.config.ts
export default defineConfig({
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ['react', 'react-dom'],
        },
      },
    },
  },
})

How to Verify Fix

  1. Deploy locally 5 times:
    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

Root Cause

Old tutorials and templates still use the deprecated Service Worker format:

// ❌ 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

// ✅ CORRECT: ES Module format
export default {
  fetch(request, env, ctx) {
    return new Response('Hello World')
  }
}

With Hono:

import { Hono } from 'hono'

const app = new Hono()

app.get('/', (c) => c.text('Hello World'))

export default app

Migration Steps

  1. Remove addEventListener:

    - addEventListener('fetch', (event) => {
    -   event.respondWith(handleRequest(event.request))
    - })
    
  2. Change to ES Module export:

    + export default {
    +   fetch(request, env, ctx) {
    +     return handleRequest(request, env)
    +   }
    + }
    
  3. Update function signatures to accept env:

    - async function handleRequest(request) {
    + async function handleRequest(request, env) {
        // Now you can access env.MY_KV, env.DB, etc.
    
  4. Update wrangler.tomlwrangler.jsonc:

    # 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:
    const value = await env.MY_KV.get('key')
    
  3. TypeScript types work:
    type Bindings = {
      MY_KV: KVNamespace
    }
    

General Troubleshooting Tips

Check Package Versions

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

rm -rf node_modules/.wrangler
rm -rf .wrangler
npm run dev

Check Wrangler Config

wrangler whoami  # Verify authentication
wrangler dev --local  # Test without deploying

Enable Verbose Logging

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

# 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:

  2. Search GitHub issues:

  3. Ask in Discord:

  4. Check Stack Overflow:

    • Tag: cloudflare-workers

All issues documented with GitHub sources All fixes production-tested