[GH-ISSUE #6036] Rate-limit retry metadata documented as seconds, but API returns milliseconds #19038

Closed
opened 2026-04-15 17:49:05 -05:00 by GiteaMirror · 2 comments
Owner

Originally created by @feliche93 on GitHub (Nov 17, 2025).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/6036

Summary

When Better Auth rejects a request due to rate limiting, the error body includes . The docs say this value is "the number of seconds until the user can make another request", but in practice (tested on 1.2.0 and 1.2.1) the value is in milliseconds. Our middleware trusted the docs and ended up telling customers to "wait 3 days" even though the limit was 15 requests/min.

Steps to reproduce

  1. Configure a small rate limit (e.g., 2 requests / 15 minutes) for an API key.
  2. Trigger the limit twice in quick succession.
  3. Inspect the thrown . Its \ value will be ~900000 for a 15-minute window.
  4. Compare that value to the doc statement ("number of seconds").

Actual vs expected

  • Actual: \ ≈ 900000 (15 minutes in milliseconds).
  • Expected (per docs): 900 seconds for a 15-minute window, or the docs should make the unit clear.

Impact

SDK consumers that follow the docs blow the retry window up by 1,000×. In our case we were formatting \ with , so users were told "please try again in 10 days" for a 15-minute throttle.

Suggested fixes

  1. Change the implementation to return seconds (matching the docs and Retry-After header), or
  2. Update the docs / API reference to state the value is milliseconds (ideally rename it to ).

Happy to provide more logs if needed!

Originally created by @feliche93 on GitHub (Nov 17, 2025). Original GitHub issue: https://github.com/better-auth/better-auth/issues/6036 ### Summary When Better Auth rejects a request due to rate limiting, the error body includes \. The docs say this value is "the number of seconds until the user can make another request", but in practice (tested on 1.2.0 and 1.2.1) the value is in **milliseconds**. Our middleware trusted the docs and ended up telling customers to "wait 3 days" even though the limit was 15 requests/min. ### Steps to reproduce 1. Configure a small rate limit (e.g., 2 requests / 15 minutes) for an API key. 2. Trigger the limit twice in quick succession. 3. Inspect the thrown \. Its \ value will be ~900000 for a 15-minute window. 4. Compare that value to the doc statement ("number of seconds"). ### Actual vs expected - **Actual:** \ ≈ 900000 (15 minutes in **milliseconds**). - **Expected (per docs):** 900 seconds for a 15-minute window, or the docs should make the unit clear. ### Impact SDK consumers that follow the docs blow the retry window up by 1,000×. In our case we were formatting \ with \, so users were told "please try again in 10 days" for a 15-minute throttle. ### Suggested fixes 1. Change the implementation to return seconds (matching the docs and Retry-After header), **or** 2. Update the docs / API reference to state the value is milliseconds (ideally rename it to \). Happy to provide more logs if needed!
GiteaMirror added the locked label 2026-04-15 17:49:05 -05:00
Author
Owner

@better-auth-agent[bot] commented on GitHub (Nov 17, 2025):

Tag @better-auth-agent if you would like an answer from the Agent.

DiagramDiscordGitHub

Diagram Join Star

<!-- gh-comment-id:3541592028 --> @better-auth-agent[bot] commented on GitHub (Nov 17, 2025): Tag @better-auth-agent if you would like an answer from the Agent. <!-- bot:webhook reply v1 --> [Diagram](https://repodiagrams.s3.eu-north-1.amazonaws.com/better-auth_ultra_detailed_interactive.html) • [Discord](https://discord.gg/better-auth) • [GitHub](https://github.com/better-auth/better-auth) [![Diagram](https://img.shields.io/badge/Diagram-2b3137?style=flat-square)](https://repodiagrams.s3.eu-north-1.amazonaws.com/better-auth_ultra_detailed_interactive.html) [![Join](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&style=flat-square)](https://discord.gg/better-auth) [![Star](https://img.shields.io/badge/star-181717?logo=github&logoColor=white&style=flat-square)](https://github.com/better-auth/better-auth)
Author
Owner

@dosubot[bot] commented on GitHub (Nov 17, 2025):

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

The Better Auth rate limiter implementation returns the retry metadata (e.g., X-Retry-After) in seconds, not milliseconds. The code divides the time difference by 1000 before sending the value, and the documentation also states this value is in seconds, matching the implementation and client-side examples see source see docs.

If you're seeing values in milliseconds (like ~900000 for a 15-minute window), it's likely coming from a custom middleware or integration layer outside the core Better Auth implementation. Double-check any wrappers or error handlers that might be reformatting or serializing the retry metadata. The official API and docs are consistent in using seconds.

To reply, just mention @dosu.


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

<!-- gh-comment-id:3541617509 --> @dosubot[bot] commented on GitHub (Nov 17, 2025): <!-- Greeting --> Hi @feliche93! I'm [Dosu](https://go.dosu.dev/dosubot) and I’m helping the better-auth team. <!-- Answer --> The Better Auth rate limiter implementation returns the retry metadata (e.g., `X-Retry-After`) in seconds, not milliseconds. The code divides the time difference by 1000 before sending the value, and the documentation also states this value is in seconds, matching the implementation and client-side examples [see source](https://github.com/better-auth/better-auth/blob/d768852258d1fea921984196806608af2983a867/packages/better-auth/src/api/rate-limiter/index.ts#L12-L200) [see docs](https://github.com/better-auth/better-auth/blob/d768852258d1fea921984196806608af2983a867/docs/content/docs/concepts/rate-limit.mdx#L21-L231). If you're seeing values in milliseconds (like ~900000 for a 15-minute window), it's likely coming from a custom middleware or integration layer outside the core Better Auth implementation. Double-check any wrappers or error handlers that might be reformatting or serializing the retry metadata. The official API and docs are consistent in using seconds. <!-- 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/91f05c04-b05e-4a88-b935-a1ad3eaa0370?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/91f05c04-b05e-4a88-b935-a1ad3eaa0370?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/91f05c04-b05e-4a88-b935-a1ad3eaa0370?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/91f05c04-b05e-4a88-b935-a1ad3eaa0370?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/91f05c04-b05e-4a88-b935-a1ad3eaa0370?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/91f05c04-b05e-4a88-b935-a1ad3eaa0370?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/91f05c04-b05e-4a88-b935-a1ad3eaa0370?feedback_type=other)</sup>&nbsp;&nbsp;[![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&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/6036)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#19038