Chapter 6How to read the errors that come back, and the limits you are most likely to meet.
Errors come back in the Connect RPC format. The HTTP status matches the code, and message carries an explanation that can be shown to the person in front of you.
HTTP/1.1 403 Forbidden
{
"code": "permission_denied",
"message": "権限がありません。自分と同等以上の権限を持つユーザーは操作できません。"
}
message comes back in Japanese today, as the samples on this page show. If your service serves another language, key your own wording off code and reason rather than off the text.
The codes
| code | HTTP | Meaning | What to do |
|---|
invalid_argument | 400 | The input is not valid | Show the message and let them correct it |
unauthenticated | 401 | Not signed in, or the credential is no longer good | Refresh the token; sign them in again if that fails |
permission_denied | 403 | Not allowed | Retrying will not help. Hide the action instead |
not_found | 404 | No such thing, or the token is not valid | Fetch the list again |
already_exists | 409 | Already registered | A duplicate email address, for instance |
failed_precondition | 412 | Something has to happen first | See the section below — the reason header says which |
resource_exhausted | 429 | A rate limit was reached | Wait before retrying. Retrying at once achieves nothing |
internal | 500 | A fault on our side | Send us the requestId |
A 500 response carries a requestId. Investigating a fault needs that value, so keep it in your own logs as well.
412: when a code is needed first
Calls that reach personal data, and calls that cannot be undone, need the account to have proved itself again. When it has not, the call is refused with 412 and an X-Step-Up-Reason header saying which of three situations it is. The three need different things from the person, so branch on the header rather than on the message text.
| X-Step-Up-Reason | Situation | What the person has to do |
|---|
step_up_enroll_required | The account has no second factor at all | Register an authenticator app first |
step_up_required | Registered, but this session has not been asked yet | Enter a code once; it lasts the session |
step_up_stale | The short window for a heavy operation has lapsed | Enter a code again, immediately before the operation |
HTTP/1.1 412 Precondition Failed
X-Step-Up-Reason: step_up_required
{
"code": "failed_precondition",
"message": "この機能を利用するには、認証コードの入力が必要です。",
"reason": "step_up_required"
}
An API key can never satisfy this, because there is nobody to ask for a code. A key that calls one of these endpoints gets 403 rather than 412, so that a client does not retry for ever.
The header is exposed through CORS, so a browser can read it. The same value is also in the body as reason.
412: other preconditions
| Situation | What it means |
|---|
| The tenant has been stopped | The tenant has no owner who can act. Reading it and leaving it still work; nothing can be changed until somebody takes it over. |
| No tenant is selected | Tenant-scoped calls act on the selected tenant. Call TenantService/SelectTenant first. |
| The account signs in with Google or Apple | It has no password, and one cannot be set. Password change and password reset are refused. |
403: what an API key may not do
| Situation | What it means |
|---|
| A read-only key attempted a change | New keys are READ_ONLY unless READ_WRITE was chosen when issuing. Issue a new key with the scope you need. |
| The call needs a code first | A key has nobody to ask for one, so it is refused outright rather than with a 412 that would invite retrying for ever. This covers far more than changes — see below. |
In practice a key can call only VerifyToken, GetCurrentUser and ListMyTenants. Everything else — including listing members and listing keys — is behind the code requirement and therefore closed to keys. Design your integration around the token verification flow rather than around reading data.
Rate limits
| What | Limit | Counted per |
|---|
| Sign-in | 5 / 15 min | account |
| Sign-in | 30 / 15 min | source address |
| Registration | 3 / hour | source address |
| Password reset request | 3 / hour | account |
| Password reset request | 10 / hour | source address |
| Token refresh | 20 / 15 min | session |
| Authenticator code attempts | 5 / 15 min | account |
| Email change request | 5 / hour | account |
| Avatar upload | 20 / hour | account |
| Request to join a tenant | 10 / hour | account |
| API key requests | 600 / min | key |
| Everything, as a ceiling | 300 / min | source address |
Sign-in attempts are limited per account as well. Failing repeatedly locks the rightful user out for a while, so do not retry automatically.
API keys get their own allowance so that a busy integration calling from one server is not held to the same ceiling as an attacker on one machine. Exceeding the address-level ceiling turns that address away for ten minutes.
Other limits
| Item | Limit |
|---|
| Access token lifetime | 15 minutes |
| Refresh token lifetime | 14 days |
| Session lifetime | 14 days, and it does not extend |
| Password length | 8–128 characters |
| Avatar image | 2MB, JPEG / PNG / WebP / GIF |
| API key lifetime | 30 days by default, 90 at most |
| Live API keys | Per tenant: 2 on Free, 3 on Light, 20 on Pro |
| Tenants one account may create | 3 by default, raisable per account |
| Members in one tenant | 10 on Free, 50 on Light, 500 on Pro. Outstanding invitations count toward it. |
| Outstanding invitations | 50 per account |
| Invitation link lifetime | 7 days |
| Tenant name | Unique among tenants that still exist |
| Email confirmation link | 24 hours |
| Password reset link | 1 hour |
| Authenticator challenge at sign-in | 5 minutes |
| Step-up window for heavy operations | 15 minutes |
| Objection period for taking over a tenant | 14 days |