Skip to content

Conversation

@hi-ogawa
Copy link
Contributor

@hi-ogawa hi-ogawa commented Jul 14, 2025

Description

todo

@hi-ogawa
Copy link
Contributor Author

hi-ogawa commented Jul 14, 2025

I found a reproduction by imitating hono node server's stream read and cancel. I think it's possible to fix this edge case on Hono side.

https://stackblitz.com/edit/stackblitz-starters-hjmrwbfj?file=index.js

import { injectRSCPayload } from 'rsc-html-stream/server';

async function main() {
  let htmlStream = new ReadableStream({
    async start(controller) {
      controller.enqueue(
        new TextEncoder().encode('<html><body>Hello World</body></html>')
      );
      controller.close();
    },
  });

  let rscStream = new ReadableStream({
    start(controller) {
      controller.enqueue(new TextEncoder().encode('[rsc]'));
      controller.close();
    },
  });

  htmlStream = htmlStream.pipeThrough(injectRSCPayload(rscStream));

  const decoder = new TextDecoder();
  const reader = htmlStream.getReader();

  // asynchronously read and cancel
  // https://github.com/honojs/node-server/blob/cb52c36d1d5d5b68416c807ce4b231c8bc549e29/src/utils.ts#L4-L42

  reader.read().then((result) => {
    console.log(result);
    console.log({ decoded: decoder.decode(result.value) });
  });

  setTimeout(() => {
    console.log('[cancel]');
    reader.cancel('boom!').catch((e) => {
      console.error('[cancel:error]', e);
    });
  }, 0);
}

main();
$ node index.js
[cancel!]
[read:result]
{ value: undefined, done: true }
{ decoded: '' }
⚠️ uncaughtException ⚠️ TypeError [ERR_INVALID_STATE]: Invalid state: Unable to enqueue
    at transformStreamDefaultControllerEnqueue (node:internal/webstreams/transformstream:284:8196)
    at TransformStreamDefaultController.enqueue (node:internal/webstreams/transformstream:284:6374)
    at flushBufferedChunks (file:///home/projects/stackblitz-starters-hjmrwbfj/node_modules/rsc-html-stream/server.js:35:18)
    at Timeout.eval (file:///home/projects/stackblitz-starters-hjmrwbfj/node_modules/rsc-html-stream/server.js:58:9)
    at <anonymous> (node:internal/timers:253:4719) {
  code: 'ERR_INVALID_STATE'
}

@hi-ogawa
Copy link
Contributor Author

hi-ogawa commented Jul 15, 2025

I went through many layers and we probably got to the bottom of it. devongovett/rsc-html-stream#10

This edge case doesn't happen on @mjackson/node-fetch-server because it never calls response.body.cancel() though I'm not sure if it's actually good thing or not https://github.com/mjackson/remix-the-web/blob/46a08d6c1c602560bf3c3927a425cf27f2d36cda/packages/node-fetch-server/src/lib/request-listener.ts#L217-L220


Actually for await (let chunk of response.body) { ... } internally calls reader.cancel() when there's an error, but it ensures that reader.read() is resolved/rejected at that point. (According to Claude)

@hi-ogawa hi-ogawa closed this Jul 15, 2025
@hi-ogawa hi-ogawa deleted the 07-14-chore_rsc_debug_rsc-html-stream_uncaught_enqueue_error branch July 15, 2025 01:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants