elysia icon indicating copy to clipboard operation
elysia copied to clipboard

onAfterResponse is called with inconsistent context object to types

Open chrisui opened this issue 3 months ago • 3 comments

What version of Elysia is running?

1.3.21

What platform is your computer?

macOS arm64

What environment are you using

Bun v1.2.21 (macOS arm64)

Are you using dynamic mode?

No

What steps can reproduce the bug?

First way to reproduce is send any empty PUT to a running elysia server where you have a onAfterResponse dependent upon some derive context value. eg. curl -X PUT http://localhost:3000/

Full repro documented and cloneable at: https://github.com/chrisui/elysia-inngest-bug

First clone the repro:

git clone [email protected]:chrisui/elysia-inngest-bug.git
  1. Install dependencies
bun install
  1. Run server
bun run index.ts
  1. In different terminal, run inngest dev server
npx inngest-cli@latest dev -u http://localhost:3000/api/inngest
  1. Access inngest dev client

Visit http://127.0.0.1:8288 in web browser

  1. See log in bun process terminal for error and unexpected stack trace
% bun run index.ts
🚀 Server running at http://localhost:3000/
Request (0 KB) {
  method: "PUT",
  url: "http://localhost:3000/api/inngest",
  headers: Headers {
    "host": "localhost:3000",
    "user-agent": "Go-http-client/1.1",
    "content-length": "43",
    "content-type": "application/json",
    "accept-encoding": "gzip",
    "x-inngest-server-kind": "dev",
  }
}
after response
19 |     )
20 |     .onAfterResponse(
21 |       { as: 'scoped' },
22 |       function loggerLogResponse({ set, log, response, start }) {
23 |         console.log('after response');
24 |         log.info(
             ^
TypeError: undefined is not an object (evaluating 'log.info')
      at loggerLogResponse (/Users/chris/Projects/elysia/inngest-bug/index.ts:24:9)
      at <anonymous> (file:///Users/chris/Projects/elysia/inngest-bug/node_modules/elysia/dist/bun/index.js:30:17)

Bun v1.2.21 (macOS arm64)

What is the expected behavior?

In the specific repro shared we should simply see a 404 as we have not even created a route yet. In general, there should not be a short-circuiting which is entering onAfterResponse too early and without sufficient context setup. At the very least, the types are incorrect.

What do you see instead?

Error because onAfterResponse appears to be called without expected context object (Eg. derive was never called)

Additional information

first posted on discord: https://discord.com/channels/1044804142461362206/1413172241474130000

Have you try removing the node_modules and bun.lockb and try again yet?

yes, and created whole new fresh minimal repro

chrisui avatar Sep 04 '25 15:09 chrisui

I've seen same behaviour when using autocannon. Explicitly using GET here.

Eg.

bunx --bun autocannon -c 100 -d 10 -m GET http://localhost:3000/health

Upon inspection I had used the wrong path here. Should've been /api/health. So that narrows down the issue to onAfterResponse being called without constructed context when elysia is in an error state.

chrisui avatar Sep 05 '25 09:09 chrisui

Looking at all the issues related to error handling and onAfterResponse I'm wondering if the types are just wrong here. Ie. in a case of an error it seems elysia does not guarantee that any of the plugin stack has been called.

Eg. in onError im forced to handle the possibility of log being undefined

Either the type needs fixing for onAfterResponse or there is a bug somewhere which prevents the prior context being created (in my case via a derive plugin).

chrisui avatar Sep 05 '25 09:09 chrisui

Did a binary search through npm versions to find this "breaks" in [email protected]. When handling a path elysia doesn't know about (so expected 404) [email protected] never calls onAfterResponse with a "bad" context object but also never calls onError.

In release logs I see https://github.com/elysiajs/elysia/issues/713 which ensures onAfterResponse gets called in event of NotFoundError which is good but then the types are likely wrong. I am unsure if Elysia is expected to have constructed the .derive() plugin chain here but it would be preferable. Since the purpose of these plugins (and especially my logger() chain) is to be orthogonal to the request chain it should be able to construct this. Otherwise how else are we supposed to pass a consistent contextual logger through our application which can be used consistently in happy and unhappy paths?

chrisui avatar Sep 05 '25 09:09 chrisui