Return type of server-side auth.api.linkSocialAccount is missing url even though it exists in runtime #1430

Closed
opened 2026-03-13 08:39:50 -05:00 by GiteaMirror · 1 comment
Owner

Originally created by @navahg on GitHub (Jun 27, 2025).

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

In the server-side, call

const response = await auth.api.linkSocialAccount({
  body: {
    provider: 'google',
    callbackURL,
  },
  headers: context.headers,
})

Current vs. Expected behavior

In the server-side, call

const response = await auth.api.linkSocialAccount({
  body: {
    provider: 'google',
    callbackURL,
  },
  headers: context.headers,
})

Here the response is of type:

{
    redirect: boolean;
    status: boolean;
} | {
    url: string;
    redirect: boolean;
}

this results in only redirect being accessible in typescript.

// accessing the following leads to a TS error
console.log(response.url)

// this won't work either
if (response.redirect) {
  console.log(response.url)
}

Perhaps, the type should be a discriminated union like:

{
    redirect: false;
    status: boolean;
} | {
    url: string;
    redirect: true;
}

?

This used to work with 1.2.9 and broke in 1.2.10.

What version of Better Auth are you using?

1.2.10

Provide environment information

- OS: MacOS
- Browser: Chrome, Safari

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

Types

Auth config (if applicable)

import { betterAuth } from "better-auth"
export const auth = betterAuth({
  account: {
    accountLinking: {
      enabled: true,
    },
  },
});

Additional context

No response

Originally created by @navahg on GitHub (Jun 27, 2025). ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce In the server-side, call ```typescript const response = await auth.api.linkSocialAccount({ body: { provider: 'google', callbackURL, }, headers: context.headers, }) ``` ### Current vs. Expected behavior In the server-side, call ```typescript const response = await auth.api.linkSocialAccount({ body: { provider: 'google', callbackURL, }, headers: context.headers, }) ``` Here the `response` is of type: ```typescript { redirect: boolean; status: boolean; } | { url: string; redirect: boolean; } ``` this results in only `redirect` being accessible in typescript. ```typescript // accessing the following leads to a TS error console.log(response.url) // this won't work either if (response.redirect) { console.log(response.url) } ``` Perhaps, the type should be a discriminated union like: ```typescript { redirect: false; status: boolean; } | { url: string; redirect: true; } ``` ? This used to work with 1.2.9 and broke in 1.2.10. ### What version of Better Auth are you using? 1.2.10 ### Provide environment information ```bash - OS: MacOS - Browser: Chrome, Safari ``` ### Which area(s) are affected? (Select all that apply) Types ### Auth config (if applicable) ```typescript import { betterAuth } from "better-auth" export const auth = betterAuth({ account: { accountLinking: { enabled: true, }, }, }); ``` ### Additional context _No response_
Author
Owner

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

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

This is a regression in 1.2.10 caused by a change to the linkSocialAccount implementation and its type definition. The current return type is a union that doesn't let TypeScript safely discriminate when the url property is present, even though at runtime url is always present when redirect is true. This broke the previous behavior from 1.2.9, where the type allowed safe access to url after checking redirect.

The correct type should be a discriminated union like:

{ redirect: false; status: boolean } | { redirect: true; url: string }

This would let you safely access url when redirect is true, matching the runtime behavior. The regression was introduced when the required fields in the return type were changed from ['url', 'redirect'] to just ['redirect'], as seen in PR #1830.

Until the type is fixed, you can work around this by using a type guard or assertion:

if (response.redirect) {
  // @ts-expect-error: url is present at runtime when redirect is true
  console.log(response.url)
}

or

if (response.redirect) {
  const url = (response as { url: string }).url
  // use url
}

This is a type-level bug and should be addressed by updating the type definition to a proper discriminated union.

To reply, just mention @dosu.


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

@dosubot[bot] commented on GitHub (Jun 27, 2025): <!-- Greeting --> Hi @navahg! I'm [Dosu](https://go.dosu.dev/dosubot) and I’m helping the better-auth team. <!-- Answer --> This is a regression in 1.2.10 caused by a change to the linkSocialAccount implementation and its type definition. The current return type is a union that doesn't let TypeScript safely discriminate when the url property is present, even though at runtime url is always present when redirect is true. This broke the previous behavior from 1.2.9, where the type allowed safe access to url after checking redirect. The correct type should be a discriminated union like: ```typescript { redirect: false; status: boolean } | { redirect: true; url: string } ``` This would let you safely access url when redirect is true, matching the runtime behavior. The regression was introduced when the required fields in the return type were changed from ['url', 'redirect'] to just ['redirect'], as seen in [PR #1830](https://github.com/better-auth/better-auth/pull/1830). Until the type is fixed, you can work around this by using a type guard or assertion: ```typescript if (response.redirect) { // @ts-expect-error: url is present at runtime when redirect is true console.log(response.url) } ``` or ```typescript if (response.redirect) { const url = (response as { url: string }).url // use url } ``` This is a type-level bug and should be addressed by updating the type definition to a proper discriminated union. <!-- 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/b2a218da-b2ea-4a10-90ef-90a734b7566a?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/b2a218da-b2ea-4a10-90ef-90a734b7566a?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/b2a218da-b2ea-4a10-90ef-90a734b7566a?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/b2a218da-b2ea-4a10-90ef-90a734b7566a?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/b2a218da-b2ea-4a10-90ef-90a734b7566a?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/b2a218da-b2ea-4a10-90ef-90a734b7566a?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/b2a218da-b2ea-4a10-90ef-90a734b7566a?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/3198)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#1430