Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🐛 BUG: Server Sent Events (SSE) causes Error: The script will never generate a response. #7767

Open
shmuli9 opened this issue Jan 14, 2025 · 0 comments
Labels
bug Something that isn't working

Comments

@shmuli9
Copy link

shmuli9 commented Jan 14, 2025

Which Cloudflare product(s) does this pertain to?

Wrangler, Miniflare, Pages

What versions are you using?

3.99.0 [wrangler], 3.20241218.0 [miniflare],

What operating system and version are you using?

Ubuntu [WSL]

Please provide a link to a minimal reproduction

No response

Describe the Bug

I have implemented server sent events (SSE) in a Remix/React Router v7 app using remix-utils and node native, EventEmitter:

import { EventEmitter } from "node:events";

import { eventStream } from "remix-utils/sse/server";
import type { Route } from "./+types/seed.logs";

export const seedEmitter = new EventEmitter();

export async function loader({ request }: Route.LoaderArgs) {
  let batchedLogs: string[] = [];
  let timeoutId: NodeJS.Timeout;

  return eventStream(request.signal, function setup(send, abort) {
    function handleLog(message: string) {
      batchedLogs.push(message);

      // Clear existing timeout
      if (timeoutId) clearTimeout(timeoutId);

      // Send batched logs every 250ms
      timeoutId = setTimeout(() => {
        if (batchedLogs.length > 0) {
          send({ event: "log", data: JSON.stringify(batchedLogs) });
          batchedLogs = [];
        }
      }, 250);
    }

    seedEmitter.on("log", handleLog);

    return function cleanup() {
      seedEmitter.off("log", handleLog);
      if (timeoutId) clearTimeout(timeoutId);
      abort();
    };
  });
}

The event emitter is shared and can be triggered elsewhere in the app. the handleLog function queues up messages for 250ms
When running this under node (eg react-router dev) it works perfectly - events stream to UI as expected (using useEventSource from remix-utils)
When building and running under workerd (eg wrangler pages dev) I get the errors:

[wrangler:inf] GET /admin/seed/logs 200 OK (13ms)
✘ [ERROR] Uncaught (in response) Error: The script will never generate a response.

✘ [ERROR] A hanging Promise was canceled. This happens when the worker runtime is waiting for a Promise from JavaScript to resolve, but has detected that the Promise cannot possibly ever resolve because all code and events related to the Promise's I/O context have already finished.

✘ [ERROR] workerd/server/server.c++:3898: error: Uncaught exception: workerd/io/io-context.c++:1209: failed: remote.jsg.Error: The script will never generate a response.

  stack:
  /home/user/code/remix-project/node_modules/.pnpm/@[email protected]/node_modules/@cloudflare/workerd-linux-64/bin/workerd@286cb40

This seems like a bug to me as EventEmitter and ReadableStream/streaming responses are officially supported (and working in remix/react-router7 generally as streaming promises works as expected).

I have tried adding a ctx.waitUntil in the functions handler, but this doesnt seem to do anything here

For completeness, this is my [[path]].ts file:

import { createPagesFunctionHandler } from "@react-router/cloudflare";

import { getLogger } from "../app/services/logger.server";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore - the server build file is generated by `remix vite:build`
// eslint-disable-next-line import/no-unresolved
import * as build from "../build/server";
import { getLoadContext } from "../load-context";

export const onRequest = async ({
  request,
  ...ctx
}: EventContext<unknown, string, unknown>) => {
  const logger = getLogger({
    request,
    context: { cloudflare: { ctx, env: ctx.env } },
  });

  const response = createPagesFunctionHandler({
    // @ts-expect-error - the types are not correct
    build,
    getLoadContext,
  })({ request, ...ctx });

  ctx.waitUntil(logger.flush());

  return response;
};

Any help/guidance is welcome. This works so nicely... when it works

Please provide any relevant error logs

[wrangler:inf] GET /admin/seed/logs 200 OK (13ms)
✘ [ERROR] Uncaught (in response) Error: The script will never generate a response.

✘ [ERROR] A hanging Promise was canceled. This happens when the worker runtime is waiting for a Promise from JavaScript to resolve, but has detected that the Promise cannot possibly ever resolve because all code and events related to the Promise's I/O context have already finished.

✘ [ERROR] workerd/server/server.c++:3898: error: Uncaught exception: workerd/io/io-context.c++:1209: failed: remote.jsg.Error: The script will never generate a response.

  stack:
  /home/user/code/remix-project/node_modules/.pnpm/@[email protected]/node_modules/@cloudflare/workerd-linux-64/bin/workerd@286cb40

Image

@shmuli9 shmuli9 added the bug Something that isn't working label Jan 14, 2025
@github-project-automation github-project-automation bot moved this to Untriaged in workers-sdk Jan 14, 2025
@shmuli9 shmuli9 changed the title 🐛 BUG: Server Sent Events cause Error: The script will never generate a response. 🐛 BUG: Server Sent Events (SSE) causes Error: The script will never generate a response. Jan 14, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something that isn't working
Projects
Status: Untriaged
Development

No branches or pull requests

1 participant