[GH-ISSUE #9195] customSession plugin incorrectly validates disableRefresh query param #19938

Open
opened 2026-04-15 19:18:36 -05:00 by GiteaMirror · 1 comment
Owner

Originally created by @guilhermegonzaga on GitHub (Apr 14, 2026).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/9195

Is this suited for github?

  • Yes, this is suited for github

Reproduction

  • Install and configure better-auth with the customSession plugin enabled.
  • Expose the auth service over HTTP (e.g., using Elysia).
  • From a client (e.g., Next.js), call getSession with the disableRefresh query parameter.

Example client call:

await authClient.getSession({
  query: {
    disableRefresh: true,
  },
  fetchOptions: {
    headers: await headers(),
  },
})

This generates the HTTP request:

GET /session?disableRefresh=true
The server responds with a validation error.

The issue only occurs when the customSession plugin is enabled.
If the plugin is removed, the same request succeeds.

Current vs. Expected behavior

Current behavior

The request fails with a validation error:

{
  "message": "[query.disableRefresh] Invalid input: expected boolean, received string",
  "code": "VALIDATION_ERROR",
  "status": 400
}

This happens because the disableRefresh query parameter is validated using:

z.boolean()

However, query parameters are always received as strings in HTTP servers.

So even when the client sends:

disableRefresh: true

The server receives:

disableRefresh: "true"

Expected behavior

The disableRefresh query parameter should accept standard HTTP query values:

disableRefresh=true disableRefresh=false

And be correctly interpreted as boolean.

Suggested fix:

z.coerce.boolean().optional()

instead of:

z.boolean().optional()

This would align with standard HTTP behavior and prevent validation errors across frameworks.

What version of Better Auth are you using?

1.6.3

System info

{
  "system": {
    "platform": "linux",
    "arch": "x64",
    "version": "#1 SMP PREEMPT_DYNAMIC Thu Jun  5 18:30:46 UTC 2025",
    "release": "6.6.87.2-microsoft-standard-WSL2",
    "cpuCount": 2
  },
  "node": {
    "version": "v22.16.0",
    "env": "development"
  },
  "packageManager": {
    "name": "bun",
    "version": "1.3.2"
  },
  "frameworks": null,
  "databases": [
    {
      "name": "pg",
      "version": "^8.17.2"
    },
    {
      "name": "drizzle",
      "version": "^0.44.7"
    }
  ],
  "betterAuth": {
    "version": "Unknown",
    "config": null,
    "error": "Cannot find module 'bun'\nRequire stack:\n- /workspaces/xxxx/src/db/schemas/auth.ts"
  }
}

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

Backend, Client

Auth config (if applicable)


Additional context

No response

Originally created by @guilhermegonzaga on GitHub (Apr 14, 2026). Original GitHub issue: https://github.com/better-auth/better-auth/issues/9195 ### Is this suited for github? - [x] Yes, this is suited for github ### Reproduction - Install and configure better-auth with the customSession plugin enabled. - Expose the auth service over HTTP (e.g., using Elysia). - From a client (e.g., Next.js), call getSession with the disableRefresh query parameter. Example client call: ```javascript await authClient.getSession({ query: { disableRefresh: true, }, fetchOptions: { headers: await headers(), }, }) ``` This generates the HTTP request: GET /session?disableRefresh=true The server responds with a validation error. The issue only occurs when the customSession plugin is enabled. If the plugin is removed, the same request succeeds. ### Current vs. Expected behavior ### Current behavior The request fails with a validation error: ```json { "message": "[query.disableRefresh] Invalid input: expected boolean, received string", "code": "VALIDATION_ERROR", "status": 400 } ``` This happens because the disableRefresh query parameter is validated using: `z.boolean()` However, query parameters are always received as strings in HTTP servers. So even when the client sends: `disableRefresh: true` The server receives: `disableRefresh: "true"` ### Expected behavior The disableRefresh query parameter should accept standard HTTP query values: `disableRefresh=true disableRefresh=false` And be correctly interpreted as boolean. Suggested fix: `z.coerce.boolean().optional()` instead of: `z.boolean().optional()` This would align with standard HTTP behavior and prevent validation errors across frameworks. ### What version of Better Auth are you using? 1.6.3 ### System info ```bash { "system": { "platform": "linux", "arch": "x64", "version": "#1 SMP PREEMPT_DYNAMIC Thu Jun 5 18:30:46 UTC 2025", "release": "6.6.87.2-microsoft-standard-WSL2", "cpuCount": 2 }, "node": { "version": "v22.16.0", "env": "development" }, "packageManager": { "name": "bun", "version": "1.3.2" }, "frameworks": null, "databases": [ { "name": "pg", "version": "^8.17.2" }, { "name": "drizzle", "version": "^0.44.7" } ], "betterAuth": { "version": "Unknown", "config": null, "error": "Cannot find module 'bun'\nRequire stack:\n- /workspaces/xxxx/src/db/schemas/auth.ts" } } ``` ### Which area(s) are affected? (Select all that apply) Backend, Client ### Auth config (if applicable) ```typescript ``` ### Additional context _No response_
GiteaMirror added the core label 2026-04-15 19:18:36 -05:00
Author
Owner

@dosubot[bot] commented on GitHub (Apr 14, 2026):

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

Good catch — the customSession plugin defines its own getSessionQuerySchema that uses z.boolean() for disableRefresh, while the main session endpoint correctly uses z.coerce.boolean() [1]. The customSession plugin's schema shadows the correct one [2].

This was fixed in PR #4020, which switched to z.coerce.boolean(). One caveat worth noting: z.coerce.boolean() mishandles the string "false" (it coerces it to true since it's a non-empty string) [3]. The disableCookieCache param in the same schema already uses a safer pattern — .or(z.string().transform((v) => v === "true")) — which correctly handles both "true" and "false" strings.

If you're on v1.6.3, upgrading should pick up the fix from PR #4020. In the meantime, you can work around it by not passing disableRefresh as a query param, or by patching the schema locally.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other
Chat with Dosu Open in Cursor Join Discord Share on X

<!-- gh-comment-id:4247555630 --> @dosubot[bot] commented on GitHub (Apr 14, 2026): <!-- Greeting --> Hi @guilhermegonzaga! I'm [Dosu](https://go.dosu.dev/dosubot) and I’m helping the better-auth team. <!-- Answer --> Good catch — the customSession plugin defines its own `getSessionQuerySchema` that uses `z.boolean()` for `disableRefresh`, while the main session endpoint correctly uses `z.coerce.boolean()` [[1]](https://github.com/better-auth/better-auth/blob/7be52d9403407378b9c2434d9f7419b51495b308/packages/better-auth/src/cookies/session-store.ts#L336-L348). The customSession plugin's schema shadows the correct one [[2]](https://github.com/better-auth/better-auth/blob/7be52d9403407378b9c2434d9f7419b51495b308/packages/better-auth/src/plugins/custom-session/index.ts#L38-L44). This was fixed in [PR #4020](https://github.com/better-auth/better-auth/pull/4020), which switched to `z.coerce.boolean()`. One caveat worth noting: `z.coerce.boolean()` mishandles the string `"false"` (it coerces it to `true` since it's a non-empty string) [[3]](https://github.com/better-auth/better-auth/issues/9099). The `disableCookieCache` param in the same schema already uses a safer pattern — `.or(z.string().transform((v) => v === "true"))` — which correctly handles both `"true"` and `"false"` strings. If you're on v1.6.3, upgrading should pick up the fix from PR #4020. In the meantime, you can work around it by not passing `disableRefresh` as a query param, or by patching the schema locally. <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=brand-link).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-great_response#message_id=da68855d-4e3b-4854-a623-f4c9d3f1bfed&feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-irrelevant_answer#message_id=da68855d-4e3b-4854-a623-f4c9d3f1bfed&feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-incorrect_sources#message_id=da68855d-4e3b-4854-a623-f4c9d3f1bfed&feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-too_verbose#message_id=da68855d-4e3b-4854-a623-f4c9d3f1bfed&feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-hallucination#message_id=da68855d-4e3b-4854-a623-f4c9d3f1bfed&feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-bug_report#message_id=da68855d-4e3b-4854-a623-f4c9d3f1bfed&feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=feedback-other#message_id=da68855d-4e3b-4854-a623-f4c9d3f1bfed&feedback_type=other)</sup> [![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=chat-badge)&nbsp;[![Open in Cursor](https://dosu.dev/dosu-in-cursor.svg)](https://cursor.com/link/prompt?text=Good%20catch%20%E2%80%94%20the%20customSession%20plugin%20defines%20its%20own%20%60getSessionQuerySchema%60%20that%20uses%20%60z.boolean%28%29%60%20for%20%60disableRefresh%60%2C%20while%20the%20main%20session%20endpoint%20correctly%20uses%20%60z.coerce.boolean%28%29%60%20%5B%5B1%5D%5D%28https%3A//github.com/better-auth/better-auth/blob/7be52d9403407378b9c2434d9f7419b51495b308/packages/better-auth/src/cookies/session-store.ts%23L336-L348%29.%20The%20customSession%20plugin%27s%20schema%20shadows%20the%20correct%20one%20%5B%5B2%5D%5D%28https%3A//github.com/better-auth/better-auth/blob/7be52d9403407378b9c2434d9f7419b51495b308/packages/better-auth/src/plugins/custom-session/index.ts%23L38-L44%29.%0A%0AThis%20was%20fixed%20in%20%5BPR%20%234020%5D%28https%3A//github.com/better-auth/better-auth/pull/4020%29%2C%20which%20switched%20to%20%60z.coerce.boolean%28%29%60.%20One%20caveat%20worth%20noting%3A%20%60z.coerce.boolean%28%29%60%20mishandles%20the%20string%20%60%22false%22%60%20%28it%20coerces%20it%20to%20%60true%60%20since%20it%27s%20a%20non-empty%20string%29%20%5B%5B3%5D%5D%28https%3A//github.com/better-auth/better-auth/issues/9099%29.%20The%20%60disableCookieCache%60%20param%20in%20the%20same%20schema%20already%20uses%20a%20safer%20pattern%20%E2%80%94%20%60.or%28z.string%28%29.transform%28%28v%29%20%3D%3E%20v%20%3D%3D%3D%20%22true%22%29%29%60%20%E2%80%94%20which%20correctly%20handles%20both%20%60%22true%22%60%20and%20%60%22false%22%60%20strings.%0A%0AIf%20you%27re%20on%20v1.6.3%2C%20upgrading%20should%20pick%20up%20the%20fix%20from%20PR%20%234020.%20In%20the%20meantime%2C%20you%20can%20work%20around%20it%20by%20not%20passing%20%60disableRefresh%60%20as%20a%20query%20param%2C%20or%20by%20patching%20the%20schema%20locally.)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot?utm_source=github&utm_medium=bot-comment&utm_campaign=github-assistant&utm_content=join-discord)&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/9195)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#19938