[GH-ISSUE #3133] [better-auth/vue] authClient.signUp.email() sends invalid JSON payload – causes "[object Object]" parsing error on backend #26808

Closed
opened 2026-04-17 17:31:10 -05:00 by GiteaMirror · 4 comments
Owner

Originally created by @Jeromearsene on GitHub (Jun 22, 2025).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/3133

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

Hi 👋

I'm using better-auth/vue and ran into a blocking issue when calling authClient.signUp.email().

🐞 Problem

When sending the following request:

await authClient.signUp.email({
  email: "test@example.com",
  password: "password1234",
  name: "test",
  image: "https://example.com/image.png",
});

The backend (using better-auth/node and toNodeHandler(auth)) throws the following error:

SyntaxError: "[object Object]" is not valid JSON

What works

If I send the exact same data using curl:

curl -X POST http://localhost:3000/api/auth/sign-up/email \
  -H "Content-Type: application/json" \
  -d '{"email":"test@example.com","password":"password1234","name":"test"}'

...everything works fine. This confirms the backend is set up correctly.

🔍 Investigation

After intercepting the outgoing request, it turns out the body is passed directly as a plain JS object, not a JSON string. This causes the browser to serialize it as [object Object], which of course is invalid JSON.

🛠️ Suggested fix

In the implementation of authClient.signUp.email, the payload should be passed through JSON.stringify(data) before being set as the body in the fetch call.
You must stringify data before.

Temporary workaround

I overrode the signUp.email method manually to fix the issue locally, but it would be ideal to have this fixed in the library itself.

Thanks for your work! Let me know if you need a repro or more details. 🙌

Current vs. Expected behavior

Current

SyntaxError: "[object Object]" is not valid JSON

What version of Better Auth are you using?

1.2.10

Provide environment information

- OS: Mac
- Backend: Express (5.1.0) with drizzle-orm (0.44.2) and trpc (11.4.2)

Which area(s) are affected? (Select all that apply)

Client

Auth config (if applicable)

import { betterAuth } from "better-auth"
export const auth = betterAuth({
  onAPIError: {
    throw: true,
    onError: (error, ctx) => {
      // Custom error handling
      console.error("=== AUTH ERROR DETAILS ===");
      console.error("Error:", error);
      console.error("Error message:", error.message);
      console.error("Error stack:", error.stack);
      console.error("Context:", ctx);
      console.error("=== END ERROR DETAILS ===");
    },
    logger: {
      level: "debug",
    },
    errorURL: "/auth/error",
  },
  database: drizzleAdapter(db, {
    debugLogs: true,
    provider: "pg",
    schema: {
      user: users,
      session: sessions,
      account: accounts,
    },
  }),
  emailAndPassword: {
    enabled: true,
    requireEmailVerification: false,
  },
  session: {
    expiresIn: 60 * 60 * 24 * 7, // 7 jours
    updateAge: 60 * 60 * 24, // 1 jour
  },
  // Ajoutez les headers CORS si nécessaire
  advanced: {
    database: {
      generateId: false,
    },
    crossSubDomainCookies: {
      enabled: false,
    },
  },
});

Additional context

No response

Originally created by @Jeromearsene on GitHub (Jun 22, 2025). Original GitHub issue: https://github.com/better-auth/better-auth/issues/3133 ### Is this suited for github? - [ ] Yes, this is suited for github ### To Reproduce Hi 👋 I'm using better-auth/vue and ran into a blocking issue when calling authClient.signUp.email(). ## 🐞 Problem When sending the following request: ```typescript await authClient.signUp.email({ email: "test@example.com", password: "password1234", name: "test", image: "https://example.com/image.png", }); ``` The backend (using better-auth/node and toNodeHandler(auth)) throws the following error: ``` SyntaxError: "[object Object]" is not valid JSON ``` ## ✅ What works If I send the exact same data using curl: ```bash curl -X POST http://localhost:3000/api/auth/sign-up/email \ -H "Content-Type: application/json" \ -d '{"email":"test@example.com","password":"password1234","name":"test"}' ``` ...everything works fine. This confirms the backend is set up correctly. ## 🔍 Investigation After intercepting the outgoing request, it turns out the body is passed directly as a plain JS object, not a JSON string. This causes the browser to serialize it as [object Object], which of course is invalid JSON. ## 🛠️ Suggested fix In the implementation of authClient.signUp.email, the payload should be passed through JSON.stringify(data) before being set as the body in the fetch call. You must stringify data before. ## ✅ Temporary workaround I overrode the signUp.email method manually to fix the issue locally, but it would be ideal to have this fixed in the library itself. Thanks for your work! Let me know if you need a repro or more details. 🙌 ### Current vs. Expected behavior ## Current `SyntaxError: "[object Object]" is not valid JSON` ### What version of Better Auth are you using? 1.2.10 ### Provide environment information ```bash - OS: Mac - Backend: Express (5.1.0) with drizzle-orm (0.44.2) and trpc (11.4.2) ``` ### Which area(s) are affected? (Select all that apply) Client ### Auth config (if applicable) ```typescript import { betterAuth } from "better-auth" export const auth = betterAuth({ onAPIError: { throw: true, onError: (error, ctx) => { // Custom error handling console.error("=== AUTH ERROR DETAILS ==="); console.error("Error:", error); console.error("Error message:", error.message); console.error("Error stack:", error.stack); console.error("Context:", ctx); console.error("=== END ERROR DETAILS ==="); }, logger: { level: "debug", }, errorURL: "/auth/error", }, database: drizzleAdapter(db, { debugLogs: true, provider: "pg", schema: { user: users, session: sessions, account: accounts, }, }), emailAndPassword: { enabled: true, requireEmailVerification: false, }, session: { expiresIn: 60 * 60 * 24 * 7, // 7 jours updateAge: 60 * 60 * 24, // 1 jour }, // Ajoutez les headers CORS si nécessaire advanced: { database: { generateId: false, }, crossSubDomainCookies: { enabled: false, }, }, }); ``` ### Additional context _No response_
GiteaMirror added the lockedbug labels 2026-04-17 17:31:10 -05:00
Author
Owner

@dosubot[bot] commented on GitHub (Jun 22, 2025):

Hi @Jeromearsene! I'm Dosu and I’m helping the better-auth team.

This is a real bug in better-auth/vue: the payload for authClient.signUp.email is sent as a plain JS object, not a JSON string, so the browser sends [object Object] instead of valid JSON. The codebase confirms that the payload is not stringified before being sent, and there’s no evidence of an automatic fix in the fetch wrapper or any recent patch that addresses this specifically. Your diagnosis and workaround (stringifying the payload before sending) are correct and necessary for now. No official fix or workaround is documented yet, so overriding the method locally is the best approach until the library is updated.
You can see the relevant implementation details in the code here:
better-auth/vue client proxy implementation.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Join Discord Share on X

<!-- gh-comment-id:2994279159 --> @dosubot[bot] commented on GitHub (Jun 22, 2025): <!-- Greeting --> Hi @Jeromearsene! I'm [Dosu](https://go.dosu.dev/dosubot) and I’m helping the better-auth team. <!-- Answer --> This is a real bug in better-auth/vue: the payload for authClient.signUp.email is sent as a plain JS object, not a JSON string, so the browser sends [object Object] instead of valid JSON. The codebase confirms that the payload is not stringified before being sent, and there’s no evidence of an automatic fix in the fetch wrapper or any recent patch that addresses this specifically. Your diagnosis and workaround (stringifying the payload before sending) are correct and necessary for now. No official fix or workaround is documented yet, so overriding the method locally is the best approach until the library is updated. You can see the relevant implementation details in the code here: [better-auth/vue client proxy implementation](https://github.com/better-auth/better-auth/blob/2fe1be0e5f023be77b07206dd960f9fd38abba8c/packages/better-auth/src/client/proxy.ts). <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/e56a736d-5998-465e-b32c-557090aaa738?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/e56a736d-5998-465e-b32c-557090aaa738?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/e56a736d-5998-465e-b32c-557090aaa738?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/e56a736d-5998-465e-b32c-557090aaa738?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/e56a736d-5998-465e-b32c-557090aaa738?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/e56a736d-5998-465e-b32c-557090aaa738?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/e56a736d-5998-465e-b32c-557090aaa738?feedback_type=other)</sup>&nbsp;&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/3133)
Author
Owner

@Jeromearsene commented on GitHub (Jun 22, 2025):

Actually, I do this to bypass problem:

const result = await authClient.$fetch("/sign-up/email", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify(payload),
      });
<!-- gh-comment-id:2994306808 --> @Jeromearsene commented on GitHub (Jun 22, 2025): Actually, I do this to bypass problem: ```typescript const result = await authClient.$fetch("/sign-up/email", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(payload), }); ```
Author
Owner

@haroon-healthera commented on GitHub (Jul 1, 2025):

I have the same issue with better-auth/react and calling signIn.emailOtp({email, otp}). I can see in Network that library sends [object Object] string as payload...

<!-- gh-comment-id:3024261157 --> @haroon-healthera commented on GitHub (Jul 1, 2025): I have the same issue with `better-auth/react` and calling `signIn.emailOtp({email, otp})`. I can see in Network that library sends `[object Object]` string as payload...
Author
Owner

@haroon-healthera commented on GitHub (Jul 2, 2025):

I have debugged this issue and found that the better-fetch lib used under the hood bails out of JSON stringifying the body if headers contain content-type header. Removing the content-type header from createAuthClient({fetchOptions: {headers}}) resolves the issue for me.

<!-- gh-comment-id:3026660771 --> @haroon-healthera commented on GitHub (Jul 2, 2025): I have debugged this issue and found that the `better-fetch` lib used under the hood bails out of JSON stringifying the `body` if headers contain `content-type` header. Removing the `content-type` header from `createAuthClient({fetchOptions: {headers}})` resolves the issue for me.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#26808