Files
2026-01-30 03:04:10 +00:00

3.6 KiB

Gotchas & Troubleshooting

Miniflare Limitations

Not supported:

  • Analytics Engine (use mocks)
  • Cloudflare Images/Stream
  • Browser Rendering API
  • Tail Workers
  • Workers for Platforms (partial support)

Behavior differences from production:

  • Runs workerd locally, not Cloudflare edge
  • Storage is local (filesystem/memory), not distributed
  • Request.cf is cached/mocked, not real edge data
  • Performance differs from edge
  • Caching implementation may vary slightly

Common Errors

"Cannot find module"

Cause: Module path wrong or modulesRules not configured
Solution:

new Miniflare({
  modules: true,
  modulesRules: [{ type: "ESModule", include: ["**/*.js"] }],
});

"Data not persisting"

Cause: Persist paths are files, not directories
Solution:

kvPersist: "./data/kv",  // Directory, not file

"Cannot run TypeScript"

Cause: Miniflare doesn't transpile TypeScript
Solution: Build first with esbuild/tsc, then run compiled JS

"request.cf is undefined"

Cause: CF data not configured
Solution:

new Miniflare({ cf: true }); // Or cf: "./cf.json"

"EADDRINUSE" port conflict

Cause: Multiple instances using same port
Solution: Use dispatchFetch() (no HTTP server) or port: 0 for auto-assign

"Durable Object not found"

Cause: Class export doesn't match config name
Solution:

export class Counter {} // Must match
new Miniflare({ durableObjects: { COUNTER: "Counter" } });

Debugging

Enable verbose logging:

import { Log, LogLevel } from "miniflare";
new Miniflare({ log: new Log(LogLevel.DEBUG) });

Chrome DevTools:

const url = await mf.getInspectorURL();
console.log(`DevTools: ${url}`); // Open in Chrome

Inspect bindings:

const env = await mf.getBindings();
console.log(Object.keys(env));

Verify storage:

const ns = await mf.getKVNamespace("TEST");
const { keys } = await ns.list();

Best Practices

✓ Do:

  • Use dispatchFetch() for tests (no HTTP server)
  • In-memory storage for CI (omit persist options)
  • New instances per test for isolation
  • Type-safe bindings with interfaces
  • await mf.dispose() in cleanup

✗ Avoid:

  • HTTP server in tests
  • Shared instances without cleanup
  • Old compatibility dates (use 2026+)

Migration Guides

From Miniflare 2.x to 3+

Breaking changes in v3+:

v2 v3+
getBindings() sync getBindings() returns Promise
ready is void ready returns Promise<URL>
service-worker-mock Built on workerd
Different options Restructured constructor

Example migration:

// v2
const bindings = mf.getBindings();
mf.ready; // void

// v3+
const bindings = await mf.getBindings();
const url = await mf.ready; // Promise<URL>

From unstable_dev to Miniflare

// Old (deprecated)
import { unstable_dev } from "wrangler";
const worker = await unstable_dev("src/index.ts");

// New
import { Miniflare } from "miniflare";
const mf = new Miniflare({ scriptPath: "src/index.ts" });

From Wrangler Dev

Miniflare doesn't auto-read wrangler.toml:

// Translate manually:
new Miniflare({
  scriptPath: "dist/worker.js",
  compatibilityDate: "2026-01-01",
  kvNamespaces: ["KV"],
  bindings: { API_KEY: process.env.API_KEY },
});

Resource Limits

Limit Value Notes
CPU time 30s default Configurable via scriptTimeout
Storage Filesystem Performance varies by disk
Memory System dependent No artificial limits
Request.cf Cached/mocked Not live edge data

See patterns.md for testing examples.