[GH-ISSUE #406] Error “Response body object should not be disturbed or locked” when sending JSON requests with better-auth in NestJS #16886

Closed
opened 2026-04-15 14:51:55 -05:00 by GiteaMirror · 8 comments
Owner

Originally created by @wh5938316 on GitHub (Nov 3, 2024).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/406

Describe the bug
I’m encountering an issue when using better-auth with drizzleAdapter in a NestJS application. When I send requests with JSON bodies to any auth-related route, I receive the following error:

ERROR [ExceptionsHandler] Request with GET/HEAD method cannot have body.
TypeError: Request with GET/HEAD method cannot have body.
    at new Request (node:internal/deps/undici/undici:6066:17)
    at getRequest (/Users/hao/workspace/bizniff-front/node_modules/.pnpm/better-call@0.2.14-beta.1/node_modules/better-call/src/adapter/request.ts:98:9)
    at <anonymous> (/Users/hao/workspace/bizniff-front/node_modules/.pnpm/better-call@0.2.14-beta.1/node_modules/better-call/src/adapter/node.ts:10:34)
    at toNestJsController (/Users/hao/workspace/bizniff-front/apps/server/dist/auth/better-auth.js:89:16)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async AuthController.handleAuth (/Users/hao/workspace/bizniff-front/apps/server/dist/auth/auth.controller.js:25:20)
    at async /Users/hao/workspace/bizniff-front/node_modules/.pnpm/@nestjs+core@10.4.1_@nestjs+common@10.4.1_reflect-metadata@0.1.14_rxjs@7.8.1__@nestjs+platfor_fcwotrhkfhfbsqcmjq6msnog44/node_modules/@nestjs/core/router/router-execution-context.js:46:28
    at async /Users/hao/workspace/bizniff-front/node_modules/.pnpm/@nestjs+core@10.4.1_@nestjs+common@10.4.1_reflect-metadata@0.1.14_rxjs@7.8.1__@nestjs+platfor_fcwotrhkfhfbsqcmjq6msnog44/node_modules/@nestjs/core/router/router-proxy.js:9:17

It seems that the issue originates from better-call, a dependency of better-auth. When better-auth processes JSON requests, it appears to encounter an issue.

To Reproduce

  1. Set up better-auth with drizzleAdapter in a NestJS application.
  2. Send a JSON request body to an auth-related route.
Originally created by @wh5938316 on GitHub (Nov 3, 2024). Original GitHub issue: https://github.com/better-auth/better-auth/issues/406 **Describe the bug** I’m encountering an issue when using better-auth with drizzleAdapter in a NestJS application. When I send requests with JSON bodies to any auth-related route, I receive the following error: ``` ERROR [ExceptionsHandler] Request with GET/HEAD method cannot have body. TypeError: Request with GET/HEAD method cannot have body. at new Request (node:internal/deps/undici/undici:6066:17) at getRequest (/Users/hao/workspace/bizniff-front/node_modules/.pnpm/better-call@0.2.14-beta.1/node_modules/better-call/src/adapter/request.ts:98:9) at <anonymous> (/Users/hao/workspace/bizniff-front/node_modules/.pnpm/better-call@0.2.14-beta.1/node_modules/better-call/src/adapter/node.ts:10:34) at toNestJsController (/Users/hao/workspace/bizniff-front/apps/server/dist/auth/better-auth.js:89:16) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) at async AuthController.handleAuth (/Users/hao/workspace/bizniff-front/apps/server/dist/auth/auth.controller.js:25:20) at async /Users/hao/workspace/bizniff-front/node_modules/.pnpm/@nestjs+core@10.4.1_@nestjs+common@10.4.1_reflect-metadata@0.1.14_rxjs@7.8.1__@nestjs+platfor_fcwotrhkfhfbsqcmjq6msnog44/node_modules/@nestjs/core/router/router-execution-context.js:46:28 at async /Users/hao/workspace/bizniff-front/node_modules/.pnpm/@nestjs+core@10.4.1_@nestjs+common@10.4.1_reflect-metadata@0.1.14_rxjs@7.8.1__@nestjs+platfor_fcwotrhkfhfbsqcmjq6msnog44/node_modules/@nestjs/core/router/router-proxy.js:9:17 ``` It seems that the issue originates from better-call, a dependency of better-auth. When better-auth processes JSON requests, it appears to encounter an issue. **To Reproduce** 1. Set up better-auth with drizzleAdapter in a NestJS application. 2. Send a JSON request body to an auth-related route.
GiteaMirror added the locked label 2026-04-15 14:51:55 -05:00
Author
Owner

@wh5938316 commented on GitHub (Nov 3, 2024):

The error seems to occur here:

My NestJS setup with better-auth is based on code from this repository. However, unlike this repository, I am using NestJS v10, and I’m unsure if this might be contributing to the issue.

<!-- gh-comment-id:2453444806 --> @wh5938316 commented on GitHub (Nov 3, 2024): The error seems to occur [here](https://github.com/Bekacru/better-call/blob/fe93bc0f1344aeafdb7dc228ea77678397b2af0f/src/adapter/request.ts#L130 ): My NestJS setup with better-auth is based on code from this [repository](https://github.com/Snazzy72/better-auth-nestjs). However, unlike this repository, I am using NestJS v10, and I’m unsure if this might be contributing to the issue.
Author
Owner

@AmineYagoub commented on GitHub (Nov 3, 2024):

Can you past your requests code here?

<!-- gh-comment-id:2453508724 --> @AmineYagoub commented on GitHub (Nov 3, 2024): Can you past your requests code here?
Author
Owner

@wh5938316 commented on GitHub (Nov 4, 2024):

@AmineYagoub Thank you for your reply

In my NestJS controller, the request.destroyed property is true. This causes the getRequest function in better-call to cancel the ReadableStream before passing it to the Request body, which leads to an error. I extracted the relevant code, and it immediately throws an error, which I believe should be reproducible.

Here’s the sample code for reference:

import { All, Controller, Next, Req, Res } from '@nestjs/common';
import { NextFunction } from 'express';
import { Request } from 'express';

@Controller('/auth')
export class AuthController {
  constructor() {}

  @All('*')
  async handleAuth(
    @Req() req: Request,
    @Res({ passthrough: true }) response: Response,
    @Next() next: NextFunction,
  ) {
    const protocol = (req.connection as any)?.encrypted ? "https" : "http";
    const base = `${protocol}://${req.headers[":authority"] || req.headers.host}`;

    console.log(req.destroyed === true ? "destroyed" : "not destroyed");

    const readable = new ReadableStream();
    readable.cancel();
    const body = readable;

    // This will throw a `TypeError: Response body object should not be disturbed or locked`
    const newReq = new Request(base + req.url, {
      // @ts-expect-error
      duplex: "half",
      method: req.method,
      body: body,
      headers: req.headers as Record<string, string>,
    });

    // Uncommented code that would handle request forwarding
    // try {
    //   return await toNestJsController(this.authService, req, response);
    // } catch (error) {
    //   next(error);
    // }
  }
}

I’m not familiar with the internal logic of better-call, so I’m unsure why it’s designed this way.

For reference, I’m using:

  • NestJS version: v10.3.10
  • Node version: v20.11.0
<!-- gh-comment-id:2453706704 --> @wh5938316 commented on GitHub (Nov 4, 2024): @AmineYagoub Thank you for your reply In my NestJS controller, the request.destroyed property is true. This causes the [getRequest](https://github.com/Bekacru/better-call/blob/83794f1cfccf8e9a49de0f7343bf5841b9106915/src/adapter/request.ts#L31) function in better-call to cancel the ReadableStream before passing it to the Request body, which leads to an error. I extracted the relevant code, and it immediately throws an error, which I believe should be reproducible. Here’s the sample code for reference: ``` import { All, Controller, Next, Req, Res } from '@nestjs/common'; import { NextFunction } from 'express'; import { Request } from 'express'; @Controller('/auth') export class AuthController { constructor() {} @All('*') async handleAuth( @Req() req: Request, @Res({ passthrough: true }) response: Response, @Next() next: NextFunction, ) { const protocol = (req.connection as any)?.encrypted ? "https" : "http"; const base = `${protocol}://${req.headers[":authority"] || req.headers.host}`; console.log(req.destroyed === true ? "destroyed" : "not destroyed"); const readable = new ReadableStream(); readable.cancel(); const body = readable; // This will throw a `TypeError: Response body object should not be disturbed or locked` const newReq = new Request(base + req.url, { // @ts-expect-error duplex: "half", method: req.method, body: body, headers: req.headers as Record<string, string>, }); // Uncommented code that would handle request forwarding // try { // return await toNestJsController(this.authService, req, response); // } catch (error) { // next(error); // } } } ``` I’m not familiar with the internal logic of better-call, so I’m unsure why it’s designed this way. For reference, I’m using: - **NestJS version**: v10.3.10 - **Node version**: v20.11.0
Author
Owner

@wh5938316 commented on GitHub (Nov 4, 2024):

@AmineYagoub

I encountered an issue with better-auth when integrating it with Nest.js. The bodyParser middleware in Nest.js reads and parses the request body before it reaches the controller, which leads to the request stream being marked as destroyed. As a result, better-auth throws an error due to the destroyed request stream.

Disabling the body parser (bodyParser: false) does resolve the issue, but this approach would require changes to the overall development setup, which is impractical for better-auth integration alone. To address this, I modified the better-call code locally, allowing better-auth to work with Nest.js by handling the parsed req.body when req.destroyed is true.

Here’s the modification I used:

fe93bc0f13/src/adapter/request.ts (L31C1-L35C3)

// if (req.destroyed) {
//   const readable = new ReadableStream();
//   readable.cancel();
//   return readable;
// }
if (req.destroyed) {
  if (req.body) {
    return new ReadableStream({
      start(controller) {
        controller.enqueue(Buffer.from(JSON.stringify(req.body)));
        controller.close();
      },
    });
  } else {
    return new ReadableStream({
      start(controller) {
        controller.close();
      },
    });
  }
}

It would be great if better-auth could provide native support or configuration options for back-end frameworks like Nest.js, where a pre-parsed request body is common.

Thank you for considering this!

<!-- gh-comment-id:2453777000 --> @wh5938316 commented on GitHub (Nov 4, 2024): @AmineYagoub I encountered an issue with better-auth when integrating it with Nest.js. The bodyParser middleware in Nest.js reads and parses the request body before it reaches the controller, which leads to the request stream being marked as destroyed. As a result, better-auth throws an error due to the destroyed request stream. Disabling the body parser (bodyParser: false) does resolve the issue, but this approach would require changes to the overall development setup, which is impractical for better-auth integration alone. To address this, I modified the better-call code locally, allowing better-auth to work with Nest.js by handling the parsed req.body when req.destroyed is true. Here’s the modification I used: https://github.com/Bekacru/better-call/blob/fe93bc0f1344aeafdb7dc228ea77678397b2af0f/src/adapter/request.ts#L31C1-L35C3 ``` // if (req.destroyed) { // const readable = new ReadableStream(); // readable.cancel(); // return readable; // } if (req.destroyed) { if (req.body) { return new ReadableStream({ start(controller) { controller.enqueue(Buffer.from(JSON.stringify(req.body))); controller.close(); }, }); } else { return new ReadableStream({ start(controller) { controller.close(); }, }); } } ``` It would be great if better-auth could provide native support or configuration options for back-end frameworks like Nest.js, where a pre-parsed request body is common. Thank you for considering this!
Author
Owner

@AmineYagoub commented on GitHub (Nov 4, 2024):

Yes exactly, we need native support for nestjs , let me start working on that

<!-- gh-comment-id:2454000057 --> @AmineYagoub commented on GitHub (Nov 4, 2024): Yes exactly, we need native support for nestjs , let me start working on that
Author
Owner

@iamstarcode commented on GitHub (Nov 4, 2024):

Oh wow! I have been faced with this issues for the past two days, It was driving me crazy,lol. I think @NoHaxito are @danyalutsevich are also looking into this. This definitely simplify things. Thanks @AmineYagoub, Looking forward to what magic you would make 😊, Thanks for the good work.

<!-- gh-comment-id:2455622293 --> @iamstarcode commented on GitHub (Nov 4, 2024): Oh wow! I have been faced with this issues for the past two days, It was driving me crazy,lol. I think @NoHaxito are @danyalutsevich are also looking into this. This definitely simplify things. Thanks @AmineYagoub, Looking forward to what magic you would make 😊, Thanks for the good work.
Author
Owner

@danyalutsevich commented on GitHub (Nov 4, 2024):

I've tried to get express app instance to attach node handler to it but I still get Invalid Url error
check my repo

Screenshot 2024-11-04 at 23 40 22
<!-- gh-comment-id:2455758117 --> @danyalutsevich commented on GitHub (Nov 4, 2024): I've tried to get express app instance to attach node handler to it but I still get Invalid Url error check my [repo](https://github.com/danyalutsevich/sandbox/blob/base/js_ts/better-auth-nestjs/src/main.ts) <img width="674" alt="Screenshot 2024-11-04 at 23 40 22" src="https://github.com/user-attachments/assets/6f8617d2-4aad-4bc6-b926-ea57864b61d1">
Author
Owner

@Bekacru commented on GitHub (Dec 17, 2024):

I'm going to close this issue. We will track NestJS support with #171

<!-- gh-comment-id:2547846720 --> @Bekacru commented on GitHub (Dec 17, 2024): I'm going to close this issue. We will track NestJS support with #171
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#16886