{"record":{"id":"82b6374a1f8a653a","repo":"langgenius/dify","slug":"the-number-of-members-has-reached-the-limit-of-you","errorCode":null,"errorMessage":"The number of members has reached the limit of your subscription.","messagePattern":"The number of members has reached the limit of your subscription\\.","errorType":"http","errorClass":null,"httpStatus":403,"severity":"warning","filePath":"api/controllers/console/wraps.py","lineNumber":202,"sourceCode":"                if dify_config.DEPLOYMENT_EDITION != DeploymentEdition.CLOUD:\n                    return view(*args, **kwargs)\n\n                vector_space = FeatureService.get_vector_space(current_tenant_id)\n                if 0 < vector_space.limit <= vector_space.size:\n                    abort(\n                        403,\n                        \"The capacity of the knowledge storage space has reached the limit of your subscription.\",\n                    )\n                return view(*args, **kwargs)\n\n            features = FeatureService.get_features(current_tenant_id, exclude_vector_space=True)\n            if features.billing.enabled:\n                members = features.members\n                apps = features.apps\n                documents_upload_quota = features.documents_upload_quota\n                annotation_quota_limit = features.annotation_quota_limit\n                if resource == \"members\" and 0 < members.limit <= members.size:\n                    abort(403, \"The number of members has reached the limit of your subscription.\")\n                elif resource == \"apps\" and 0 < apps.limit <= apps.size:\n                    abort(403, \"The number of apps has reached the limit of your subscription.\")\n                elif resource == \"documents\" and 0 < documents_upload_quota.limit <= documents_upload_quota.size:\n                    # The api of file upload is used in the multiple places,\n                    # so we need to check the source of the request from datasets\n                    source = request.args.get(\"source\") or request.form.get(\"source\")\n                    if source == \"datasets\":\n                        abort(403, \"The number of documents has reached the limit of your subscription.\")\n                    else:\n                        return view(*args, **kwargs)\n                elif resource == \"workspace_custom\" and not features.can_replace_logo:\n                    abort(403, \"The workspace custom feature has reached the limit of your subscription.\")\n                elif resource == \"annotation\" and 0 < annotation_quota_limit.limit < annotation_quota_limit.size:\n                    abort(403, \"The annotation quota has reached the limit of your subscription.\")\n                else:\n                    return view(*args, **kwargs)\n\n            return view(*args, **kwargs)","sourceCodeStart":184,"sourceCodeEnd":220,"githubUrl":"https://github.com/langgenius/dify/blob/ef8544b173fd6cd7a8e71df2cab576e52bebbfbc/api/controllers/console/wraps.py#L184-L220","documentation":"HTTP 403 from `cloud_edition_billing_resource_check(\"members\")`. Inside the decorator, when `resource == \"members\"` and `0 < members.limit <= members.size`, it aborts with `The number of members has reached the limit of your subscription.` Invite/add-member operations are blocked at the quota ceiling.","triggerScenarios":"Inviting a new member (or any operation gated by `cloud_edition_billing_resource_check(\"members\")`) on a tenant whose seat count has reached the plan's seat limit.","commonSituations":"Team plan with a 3-seat limit trying to add a 4th member; bulk invite pushing count over the cap; stale features cache making `size` undercount (less common).","solutions":["Upgrade the workspace plan to raise the seat limit.","Remove an inactive member to free a seat before inviting a new one.","Check `FeatureService.get_features(...).members` (`limit` vs `size`) in the UI and warn before the user attempts to invite.","Confirm the features cache is fresh if you believe the count is wrong."],"exampleFix":"// before\nawait inviteMember(email)  // 403 when at seat limit\n// after\nconst { members } = await getFeatures()\nif (members.limit > 0 && members.size >= members.limit) {\n  alert('Seat limit reached — upgrade or remove a member.')\n} else {\n  await inviteMember(email)\n}","handlingStrategy":"validation","validationCode":"from services.feature_service import FeatureService\n\n_, tenant_id = current_account_with_tenant()\nfeatures = FeatureService.get_features(tenant_id, exclude_vector_space=True)\nm = features.members\n\ndef can_add_member() -> bool:\n    return not (m.limit > 0 and m.size >= m.limit)\n\nif not can_add_member():\n    # surface upgrade / remove-member prompt instead of inviting\n    ...","typeGuard":"def seats_available(limit: int, size: int) -> bool:\n    return not (limit > 0 and size >= limit)","tryCatchPattern":"try:\n    resp = client.post(\"/workspace/members/invite-email\", ...)\nexcept HTTPError as err:\n    if err.response.status_code == 403 and \"members\" in err.response.text:\n        # seat limit reached — upgrade or remove a member\n        ...\n    raise","preventionTips":["Show current/limit seat count in the UI; disable invite at the ceiling.","Remove inactive members to free seats before inviting.","Upgrade the plan to raise the seat limit.","Refresh features cache if the seat count looks stale."],"tags":["billing","quota","members","http-403"],"backgroundTag":null,"analyzedSha":"ef8544b173fd6cd7a8e71df2cab576e52bebbfbc","analyzedAt":"2026-08-12T05:15:17.394Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}