Skip to content

fix(auth): stop the users limit card silently blocking Update - #3184

Merged
HarshMN2345 merged 2 commits into
mainfrom
fix-users-limit-update-button
Aug 25, 2026
Merged

fix(auth): stop the users limit card silently blocking Update#3184
HarshMN2345 merged 2 commits into
mainfrom
fix-users-limit-update-button

Conversation

@HarshMN2345

@HarshMN2345 HarshMN2345 commented Aug 25, 2026

Copy link
Copy Markdown
Member

What

Auth > Security > Users limit leaves Update disabled with nothing explaining why whenever the entered value is one the API cannot accept.

Reported on Discord: on a project with no limit, selecting Limited and entering 0 does nothing — the button never enables, so the change cannot be saved.

Why

The user-limit policy reserves total: 0 for "no limit" — from the SDK: "Value can be between 0 and 10000. Use 0 or null to disable the limit." The card let you type 0 as though it were a limit, and the disabled check conflated the two states:

(!isLimited && policy.total === 0) || (isLimited && policy.total === newLimit)

With policy.total === 0 and newLimit === 0, that is 0 === 0 — Update disabled. The button looks broken; it is actually refusing a state the API cannot represent, without saying so.

The same check had three more holes:

Input Before
Limited 0, project already limited to 100 Update enabled, saved total: 0, silently turning the limit off while the UI still read "Limited 0"
Field cleared Binding yields undefined → Update enabled → SDK throws Missing required parameter: "total" as an error toast
Negative value No min on the input → Update enabled → 400 from the API

Changes

  • Move the valid range (1–10000) and the changed-vs-saved comparison into usersLimit.ts.
  • Put min / max / step on the input.
  • Show a rejected value as an inline error on the field instead of a dead button: "Enter a whole number between 1 and 10000, or select "Unlimited" to allow any number of users."
  • 0 can no longer be submitted as a limit, so the card cannot drift out of sync with the saved policy.
  • Drop maxUsersInputField and its focus effect — nothing ever bound it, so it was always null and never ran. Wiring it up would have auto-focused and scrolled to the card on every page load.

Tests

usersLimit.test.ts covers the reported repro, the silent 0-saves-as-unlimited path, cleared / decimal / out-of-range input, and the valid transitions. Colocated plain-vitest, same pattern as rows/store.test.ts — no jsdom or component-test scaffolding needed.

format, check (0 errors), lint (0 errors), test:unit (275 passed) and build are clean.

Note for the original question

Setting the users limit to 0 to block client-side signups cannot work by design — 0 disables the policy. Verified against the server:

  • APP_LIMIT_USERS = 10_000 (app/init/constants.php), matching the range this card now enforces.
  • The limit is checked in app/controllers/api/account.php only — six call sites covering email/password, magic URL, OAuth, phone and anonymous, so "regardless of authentication method" holds for the client Account API.
  • Platform/Modules/Users/Http/Users/Create.php has no such check, so an API key can still create users past the limit.

So the workaround suggested in the thread does hold: set the limit to 1, create one user yourself, and an Appwrite Function using an API key keeps creating users through the Users API while client-side signup returns user_count_exceeded.

The user-limit policy reserves `total: 0` for "no limit", but the card
treated 0 as a value you could type. On a project with no limit,
selecting Limited and entering 0 left `policy.total === newLimit`, so
Update stayed disabled with nothing explaining why.

The same check had three more holes: entering 0 on an already-limited
project saved `total: 0` and silently turned the limit off while the UI
still read "Limited 0"; clearing the field bound `undefined` and made
the SDK throw `Missing required parameter: "total"`; and with no `min`
on the input a negative value reached the API as a 400.

Move the range and changed-vs-saved checks into usersLimit.ts, put
min/max/step on the input, and surface a rejected value as an inline
error instead of a dead button. 0 can no longer be submitted as a
limit, so the card cannot drift out of sync with the saved policy.

Also drop `maxUsersInputField` and its focus effect: nothing ever bound
it, so it was always null and never ran. Wiring it up would have
auto-focused and scrolled to the card on every page load.
@appwrite

appwrite Bot commented Aug 25, 2026

Copy link
Copy Markdown

Console (appwrite/console)

Project ID: 688b7bf400350cbd60e9

Sites (1)
Site Status Logs Preview QR
 console-stage
688b7cf6003b1842c9dc
Ready Ready View Logs Preview URL QR Code

Tip

Every Git commit and branch gets its own deployment URL automatically

@greptile-apps

greptile-apps Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR prevents invalid user-limit values from silently disabling or misusing the Update action.

  • Centralizes users-limit range, integer validation, and changed-state calculation.
  • Adds input constraints and an inline validation message.
  • Adds unit coverage for valid, unchanged, empty, fractional, zero, and out-of-range values.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
src/routes/(console)/project-[region]-[project]/auth/security/updateUsersLimit.svelte Integrates centralized validation into the user-limit control, displays field errors, and blocks invalid submissions.
src/routes/(console)/project-[region]-[project]/auth/security/usersLimit.ts Defines the valid limited-mode range and consistently derives validation and disabled state.
src/routes/(console)/project-[region]-[project]/auth/security/usersLimit.test.ts Covers the reported zero-value failure and the major valid, invalid, unchanged, and unlimited transitions.

Reviews (2): Last reviewed commit: "test(auth): assert users limit button st..." | Re-trigger Greptile

The button-state cases reimplemented the card's `btnDisabled` expression
inside the test file, so they asserted against a copy of the logic. The
guard could be dropped from the component and every test naming the
reported bug would still pass.

Fold the derivation into `usersLimitState()`, render the card from it,
and drive the tests through it. Verified by mutation: dropping the error
guard, dropping the limited-mode guard, and ignoring mode in the
changed-check now fail 1, 1 and 2 cases; against the old tests all three
passed clean.
@HarshMN2345
HarshMN2345 merged commit 0e69386 into main Aug 25, 2026
3 of 4 checks passed
@HarshMN2345
HarshMN2345 deleted the fix-users-limit-update-button branch August 25, 2026 12:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants