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

From Discord: Advice on understanding Errors stripped by Effect #4538

Open
effect-bot opened this issue Feb 28, 2025 · 8 comments
Open

From Discord: Advice on understanding Errors stripped by Effect #4538

effect-bot opened this issue Feb 28, 2025 · 8 comments
Assignees

Comments

@effect-bot
Copy link

Summary

The user is experiencing issues with an error in their project, which they suspect might originate from either a misconfigured Drizzle client or incompatibilities with Bun. However, they find that Effect, a TypeScript library, seems to obscure or "swallow" the error, making it difficult to trace.

The user shared some code related to converting a stream to a readable stream using Effect. They mentioned placing a debugger around the toReadableStreamRuntime function implementation to diagnose the issue.

They found a specific commit made by Tim Smart in the Effect library and discovered that reverting to earlier package dependencies (before the commit was included) resolves their error.

Key Takeaways:

  1. Error Swallowing: External libraries, like Effect, can sometimes obscure errors, making debugging challenging. It can require diving into library code or looking at changelogs and commits.
  2. Version Control: Using version control to revert to earlier versions of dependencies can help identify when a problem was introduced.
  3. Diagnostic Techniques: Placing debuggers and using tools like logging can aid in narrowing down where errors might occur within complex integrations.
  4. Community Contributions: Keeping track of changes in open-source libraries, like commits from community contributors, can provide insights into potential sources of errors.

Discord thread

https://discord.com/channels/795981131316985866/1345131939056848957

@colelawrence
Copy link

colelawrence commented Feb 28, 2025

Query: select "id" from "org_users" where "org_users"."id" = $1 limit $2 -- params: ["371b9988-3db5-4a47-9d76-5d37c87679df", 1]
2142 |         currentResolve();
2143 |         currentResolve = undefined;
2144 |       }))));
2145 |       fiber.addObserver(exit => {
2146 |         if (exit._tag === "Failure") {
2147 |           controller.error(Cause.squash(exit.cause));
                 ^
TypeError: Value of "this" must be of type ReadableStreamDefaultController
 code: "ERR_INVALID_THIS"

      at <anonymous> (/Users/cole/phosphor/phosphor-genius/node_modules/effect/dist/esm/internal/stream.js:2147:22)
      at setExitValue (/Users/cole/phosphor/phosphor-genius/node_modules/effect/dist/esm/internal/fiberRuntime.js:646:23)
      at evaluateEffect (/Users/cole/phosphor/phosphor-genius/node_modules/effect/dist/esm/internal/fiberRuntime.js:757:20)
      at evaluateMessageWhileSuspended (/Users/cole/phosphor/phosphor-genius/node_modules/effect/dist/esm/internal/fiberRuntime.js:708:16)
      at drainQueueOnCurrentThread (/Users/cole/phosphor/phosphor-genius/node_modules/effect/dist/esm/internal/fiberRuntime.js:486:85)
      at run (/Users/cole/phosphor/phosphor-genius/node_modules/effect/dist/esm/internal/fiberRuntime.js:1156:10)
      at starveInternal (/Users/cole/phosphor/phosphor-genius/node_modules/effect/dist/esm/Scheduler.js:68:15)

Weird summary, but something is broken between "effect": "3.12.7" and "effect": "3.13.4". I'm bisectiong, now...

Working together:

    "@effect/cli": "0.54.1",
    "@effect/language-service": "0.2.0",
    "@effect/platform": "0.75.1",
    "@effect/platform-bun": "0.55.1",
    "@effect/rpc": "0.50.1",
    "@effect/rpc-http": "0.48.1",
    "effect": "3.12.7",

Not working:

    "@effect/cli": "0.56.4",
    "@effect/language-service": "0.2.0",
    "@effect/platform": "0.77.4",
    "@effect/platform-bun": "0.57.4",
    "@effect/rpc": "0.52.4",
    "@effect/rpc-http": "0.50.4",
    "effect": "3.13.4",

@colelawrence
Copy link

@tim-smart - I updated the comment, the error seems to have been introduced between "effect": "3.12.7" and "effect": "3.13.4"–so not necessarily related to the commit for optimizing toStream

@tim-smart
Copy link
Contributor

If you send a small reproduction, then I can take a look.

@colelawrence
Copy link

Sent details privately on Discord - https://discord.com/channels/795981131316985866/1253429846063841411/1345148845612470412

  // 3.12.7 -> ok
  // 3.12.8 -> impossible to test due to missing effect/internal/cause required from effect/Schema.js
  // 3.12.9 -> broken
  // 3.12.12 -> broken
  // 3.13.0 -> broken  

@colelawrence
Copy link

colelawrence commented Feb 28, 2025

When I manually patch revert 9172efb
from node_modules/effect/dist/esm/internal/stream.js using all recent latest versions of effect, the error doesn't occur. @tim-smart

Sorry I don't know what is going on under the hood to create a test case that trips this issue

EDIT: here's the patch to avoid the issue:

diff --git a/dist/esm/internal/stream.js b/dist/esm/internal/stream.js
index 3621784d5c285bbab1449c79ff16e3fb355f4cbf..297a221618693aeaae7477a70f086f6c4e0fe9b7 100644
--- a/dist/esm/internal/stream.js
+++ b/dist/esm/internal/stream.js
@@ -8,6 +8,7 @@ import * as Effect from "../Effect.js";
 import * as Either from "../Either.js";
 import * as Equal from "../Equal.js";
 import * as Exit from "../Exit.js";
+import * as Scope from "../Scope.js";
 import * as Fiber from "../Fiber.js";
 import * as FiberRef from "../FiberRef.js";
 import { constTrue, dual, identity, pipe } from "../Function.js";
@@ -2128,37 +2129,51 @@ export const toReadableStream = /*#__PURE__*/dual(args => isStream(args[0]), (se
 export const toReadableStreamEffect = /*#__PURE__*/dual(args => isStream(args[0]), (self, options) => Effect.map(Effect.runtime(), runtime => toReadableStreamRuntime(self, runtime, options)));
 /** @internal */
 export const toReadableStreamRuntime = /*#__PURE__*/dual(args => isStream(args[0]), (self, runtime, options) => {
+  const runSync = Runtime.runSync(runtime)
   const runFork = Runtime.runFork(runtime);
-  let currentResolve = undefined;
-  let fiber = undefined;
-  const latch = Effect.unsafeMakeLatch(false);
+  let pull//: Effect.Effect<void, never, R>
+  let scope//: Scope.CloseableScope
   return new ReadableStream({
     start(controller) {
-      fiber = runFork(runForEachChunk(self, chunk => latch.whenOpen(Effect.sync(() => {
-        latch.unsafeClose();
-        for (const item of chunk) {
-          controller.enqueue(item);
-        }
-        currentResolve();
-        currentResolve = undefined;
-      }))));
-      fiber.addObserver(exit => {
-        if (exit._tag === "Failure") {
-          controller.error(Cause.squash(exit.cause));
-        } else {
-          controller.close();
-        }
-      });
+      scope = runSync(Scope.make())
+      const pullChunk = pipe(
+        toPull(self),
+        Scope.extend(scope),
+        runSync,
+        Effect.flatMap((chunk) => Chunk.isEmpty(chunk) ? pullChunk : Effect.succeed(chunk))
+      )
+      pull = pipe(
+        pullChunk,
+        Effect.tap((chunk) =>
+          Effect.sync(() => {
+            Chunk.map(chunk, (a) => {
+              controller.enqueue(a)
+            })
+          })
+        ),
+        Effect.tapErrorCause(() => Scope.close(scope, Exit.void)),
+        Effect.catchTags({
+          "None": () =>
+            Effect.sync(() => {
+              controller.close()
+            }),
+          "Some": (error) =>
+            Effect.sync(() => {
+              controller.error(error.value)
+            })
+        }),
+        Effect.asVoid
+      )
     },
     pull() {
       return new Promise(resolve => {
-        currentResolve = resolve;
-        Effect.runSync(latch.open);
+        runFork(pull, { scope }).addObserver((_) => resolve())
       });
     },
     cancel() {
-      if (!fiber) return;
-      return Effect.runPromise(Effect.asVoid(Fiber.interrupt(fiber)));
+      return new Promise((resolve) => {
+        runFork(Scope.close(scope, Exit.void)).addObserver((_) => resolve())
+      })
     }
   }, options?.strategy);
 });

@tim-smart
Copy link
Contributor

And what code will trigger the issue? And what runtime? Just so I can make the issue appear locally.

@colelawrence
Copy link

@tim-smart - I do not know exactly what part of the codebase triggers this. My suspicion is that it's triggered by something related to http-rpc receiving or serving a response The platform is bun.

@colelawrence
Copy link

colelawrence commented Mar 1, 2025

Extremely sneaky... the error occurs here when reporting the error itself into the ReadableStreamDefaultController @tim-smart
Image
Image

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

No branches or pull requests

3 participants