cloudflare / miniflare

πŸ”₯ Fully-local simulator for Cloudflare Workers. For the latest version, see https://github.com/cloudflare/workers-sdk/tree/main/packages/miniflare.
https://miniflare.dev
MIT License
3.78k stars 205 forks source link

Error using `miniflare -w`; `FetchError: No fetch handler` after rebuild #34

Closed TimTinkers closed 3 years ago

TimTinkers commented 3 years ago

I am on Windows. While stumbling my way to the error in #33 I also encountered this issue with Miniflare failing on rebuild with the watcher. I am including logs that demonstrate starting my worker with miniflare -w, making a request to my worker which failed due to #33, then removing the problematic caching code. At this point the worker is rebuilt automatically. The call should succeed now. When I make another request to my worker, the call fails with FetchError: No fetch handler responded and unable to proxy request to upstream: no upstream specified. When I terminate Miniflare and restart it, the same call will succeed as expected. Here are my logs:

$ miniflare -w

up to date, audited 92 packages in 672ms

8 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

> file@1.0.0 build
> rm -rf ./dist && rollup -c

./src/main.mjs β†’ ./dist/main.mjs...
created ./dist/main.mjs in 245ms
[mf:inf] Build succeeded
[mf:inf] Worker reloaded!
[mf:inf] Listening on :8787
[mf:inf] - http://192.168.3.211:8787
[mf:inf] - http://127.0.0.1:8787
[mf:err] TypeError: headers is not iterable
    at normaliseHeaders (C:\Users\tim\AppData\Local\Volta\tools\image\packages\miniflare\node_modules\miniflare\src\kv\cache.ts:28:30)
    at Cache.put (C:\Users\tim\AppData\Local\Volta\tools\image\packages\miniflare\node_modules\miniflare\src\kv\cache.ts:62:24)
    at C:\Users\tim\Documents\Choir\Choir-Platform\file\dist\main.mjs:1:565
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at EventsModule.dispatchFetch (C:\Users\tim\AppData\Local\Volta\tools\image\packages\miniflare\node_modules\miniflare\src\modules\events.ts:149:27)
    at Miniflare._Miniflare_httpRequestListener (C:\Users\tim\AppData\Local\Volta\tools\image\packages\miniflare\node_modules\miniflare\src\index.ts:408:20)
GET /file/instrument-samples/piano/A2.mp3 200 OK (8.34ms)

up to date, audited 92 packages in 677ms

8 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

> file@1.0.0 build
> rm -rf ./dist && rollup -c

[mf:err] Unable to read dist\main.mjs: Error: ENOENT: no such file or directory, open 'C:\Users\tim\Documents\Choir\Choir-Platform\file\dist\main.mjs' (defaulting to empty string)
[mf:inf] Worker reloaded!

./src/main.mjs β†’ ./dist/main.mjs...
created ./dist/main.mjs in 249ms
[mf:inf] Build succeeded
[mf:err] GET /file/instrument-samples/piano/A2.mp3: FetchError: No fetch handler responded and unable to proxy request to upstream: no upstream specified. Have you added a fetch event listener that responds with a Response?
    at EventsModule.dispatchFetch (C:\Users\tim\AppData\Local\Volta\tools\image\packages\miniflare\node_modules\miniflare\src\modules\events.ts:167:13)
    at Miniflare.dispatchFetch (C:\Users\tim\AppData\Local\Volta\tools\image\packages\miniflare\node_modules\miniflare\src\index.ts:266:39)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at Miniflare._Miniflare_httpRequestListener (C:\Users\tim\AppData\Local\Volta\tools\image\packages\miniflare\node_modules\miniflare\src\index.ts:408:20)
GET /file/instrument-samples/piano/A2.mp3 500 Internal Server Error (67.11ms)
^C

$ miniflare

up to date, audited 92 packages in 661ms

8 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

> file@1.0.0 build
> rm -rf ./dist && rollup -c

./src/main.mjs β†’ ./dist/main.mjs...
created ./dist/main.mjs in 249ms
[mf:inf] Build succeeded
[mf:inf] Worker reloaded!
[mf:inf] Listening on :8787
[mf:inf] - http://192.168.3.211:8787
[mf:inf] - http://127.0.0.1:8787
GET /file/instrument-samples/piano/A2.mp3 200 OK (9.51ms)
mrbbot commented 3 years ago

Hello! πŸ‘‹ I think the issue here is the rm -rf ./dist in your build script. When Miniflare detects a change in your build watch path, it will trigger a rebuild but won't reload the worker. The worker is only reloaded when your script changes (Miniflare assumes this means the build has completed). However, in your case, you're removing the dist directory, so Miniflare thinks the build is done and tries to load an empty script with no fetch handler.

You should be able to fix this by removing the rm, since Rollup overwrites output files. This is definitely a bug though, Miniflare knows when the build actually finishes, so it should reload the worker then instead.

mrbbot commented 3 years ago

Just released version 1.4.0 with this fix. Please let me know if you have any other issues or feedback. πŸ˜„

evrys commented 2 years ago

Hmm, I'm still running into this issue on Windows with 1.4.1 (but not macos, where it works as expected). I run webpack and miniflare together with concurrently, the two processes look like this:

{
    "server-webpack": "webpack -w --config webpack.server.config.js",
    "server-miniflare": "miniflare --kv=STORE --watch --debug server/devdist/worker.js"
}

After any code change, all requests throw FetchError: no fetch handler responded

I don't thiink I'm removing devdist at any point, unless webpack does that by default somehow.

Ideally, I would prefer miniflare not to handle the build part and instead just watch the output, because it's faster DX to run webpack watch separately than to have miniflare reinvoke a webpack build from scratch each time (due to the startup overhead).

I think the problem might be that it reloads worker.js when webpack starts writing to it, instead of when it finishes? The log looks like this:

[server-webpack] webpack 5.56.0 compiled successfully in 95 ms
[server-miniflare] [mf:dbg] server\devdist\worker.js changed, reloading...
[server-miniflare] [mf:dbg] Reloading server\devdist\worker.js...
[server-miniflare] [mf:inf] Worker reloaded! (0B)
[server-webpack] asset worker.js 1.73 MiB [emitted] (name: worker) 1 related asset
evrys commented 2 years ago

I've put in a pull request to fix this (at least for my particular scenario, hopefully also others) by using chokidar's awaitWriteFinish option to check that the file isn't actively in the process of being altered before reloading it.

Kostanos commented 1 year ago

Hey, the issue seems to be very old, but I'm having the same problem. Trying to make miniflare to work with ES6 module and no luck yet. Tried with both, webpack and esbuild.

image

Any idea?

PS. removing build instructions and running with wrangler dev works as expected.

mrbbot commented 1 year ago

Hey @Kostanos! It looks like you've got the -m flag enabled which expects a modules format (export default { fetch() { ... } }) Worker. I'm guessing you're using a service worker format (addEventListener("fetch", ...)) Worker instead. Try removing -m from your Miniflare command. πŸ‘

Kostanos commented 1 year ago

Actually I'm using the "module" type, here is a small shot of my code.

// eslint-disable-next-line import/no-unresolved
import { verifyIdToken } from 'web-auth-library/google';
import { v4 as uuidv4 } from 'uuid';
import { parseToken } from './helpers';

export default {
  /**
   *
   * @param {import("@cloudflare/workers-types/experimental").Request} request
   * @param {object} env
   * @param {import("@cloudflare/workers-types/experimental").ExecutionContext} _ctx
   * @returns {Promise<Response>}
   */
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  async fetch(request, env, _ctx) {
    // Only accept a PUT request
    if (request.method !== 'PUT') {
      return new Response('{"error": "Bad request!"}');
    }
    let envCheck;
    if (!((envCheck = 'GOOGLE_CLOUD_CREDENTIALS') in env)) throw new Error(`Missing ${envCheck} environment variable`);
    if (!((envCheck = 'AUTH_KEY_SECRET') in env)) throw new Error(`Missing ${envCheck} environment variable`);
    if (!((envCheck = 'R2_BUCKET') in env)) throw new Error(`Missing ${envCheck} environment variable`);

    try {
      const idToken = parseToken(request);
    ...

BTW. I decided to give up with cloudflare server-less for the moment and will implement the traditional micro-services architecture with nodejs. I spent 3-4 days to make the following stack work together:

and I failed. I've tried both, wrangler dev and miniflare, each of them has its own issue... There is so much workarounds and hacks need to be done to make it works.

With micro-services I can make it work in one day.

Thank you for your help anyway.

aseure commented 1 year ago

Any news on this? I'm facing the same issue as @Kostanos:

And I'm also getting FetchError [ERR_NO_HANDLER]: No fetch handler defined and no upstream to proxy to specified.

mxschumacher commented 1 year ago

Hello - I'm struggling with the same issue when running npm run dev, which in my case is: miniflare --watch --debug --modules

I'm using wrangler 3.0.0 and am trying to make the module syntax work.

The NODE_OPTIONS=--experimental-vm-modules should only apply to API usage, e.g. not if you use miniflare as a CLI.

When I use wrangler dev --local, I run into the issue described here: https://github.com/cloudflare/workers-sdk/issues/3262 - not sure whether they are related?

I would say we should reopen this thread.