OAuth Errors
Canonical reference for every OAuth error code Loopwise returns, the cause, and how to fix it.
Loopwise's RFC 6749-style OAuth error responses (responses whose payload includes an OAuth error code like invalid_scope) carry three fields:
{
"error": "invalid_scope",
"error_description": "Human-readable explanation with concrete fix hints.",
"error_uri": "https://docs.loopwise.com/reference/errors#invalid_scope"
}The error_uri field follows RFC 6749 §5.2 — optional in the spec, but Loopwise populates it on every RFC 6749 OAuth error so agents reading the JSON have a deterministic URL to fetch for context. The anchor in error_uri points at the matching section on this page.
Non-OAuth gateway errors (e.g. an unauthenticated request hitting the Next.js proxy before the token endpoint sees it) follow a different shape and don't carry error_uri — only OAuth-protocol responses do.
This page is the canonical source the error_uri field points at — anchor on each error code below matches the slug an integrator's error_uri will land on.
access_denied
The user or the authorization server denied the request. Three causes are possible:
- The user clicked Deny on the consent screen. They must restart the authorize flow and click Allow.
- The user's grant was revoked — either by the user themselves from
/oauth/authorized_applications, or by a school admin. The user must re-authorize. - A scope your application requests was removed from the client's allowed-scopes list between authorize and callback. Check
/oauth/applicationsfor the current allowed-scope set on this client.
invalid_client
Client authentication failed. Three branches:
client_idnot found. Confirm the value you're sending exactly matches what's shown at/oauth/applications— no trailing whitespace, no truncation.client_secretwrong. The client exists but the secret was rejected. Either your.envis out of date, or the secret was regenerated. Open the client at/oauth/applicationsto verify or regenerate.- Audience mismatch. The client was found, but its audience (
staff/org_admin/member) doesn't match the flow you're executing. Amember-audience client cannot run a staff flow; astaff-audience client cannot access account-only flows.
invalid_grant
The provided authorization grant is invalid, expired, or revoked. Two common causes:
- Refresh token rotation. Loopwise rotates refresh tokens — every
/api/oauth/tokenresponse returns a newrefresh_tokenthat must replace the old one. The previous token is revoked immediately. If you've reused a refresh token (e.g. a process crash lost the rotation), the next call returnsinvalid_grantand the user must re-authorize. - Authorization code single-use + 10-minute expiry. Codes can be exchanged for tokens exactly once and only within 10 minutes of issuance. Exchange the code as soon as your callback runs.
invalid_redirect_uri
The redirect_uri you sent is malformed or not whitelisted on the OAuth client. To fix:
- Open
/oauth/applications, navigate to this OAuth client, and copy the exact entries in Redirect URIs. - Compare to what you're sending: scheme (
httpvshttps), host, port, and full path must all match — none of them are normalized.
Common mistake — better-auth / genericOAuth callback path. Better-auth's genericOAuth plugin generates a callback path that includes an /oauth2/ segment:
https://yourapp.com/api/auth/oauth2/callback/loopwise
^^^^^^^Not /api/auth/callback/loopwise. This is the most common single source of invalid_redirect_uri reports. Use getLoopwiseRedirectURI({ baseURL }) from @loopwise/admin-sdk/better-auth to get the right URL to register.
invalid_request
The request is missing a required parameter, includes an unsupported parameter value, or is otherwise malformed. The error_description will name the specific parameter when the cause is missing/malformed input.
invalid_scope
The requested scope is invalid, unknown, or not enabled for this OAuth application. To diagnose:
- Fetch
/.well-known/oauth-authorization-serverand checkscopes_supported— that's the universe of scopes a discovery-aware client can request. - Open the client at
/oauth/applicationsand check its allowed-scope list — the intersection withscopes_supportedis what this client may actually request.
Common mistake. Use students:read or members:read — not members:list. The Loopwise scope grammar is resource:action; verbs are read / write, not list.
Special case — userinfo endpoint. /api/oauth/userinfo returns invalid_scope when the token is missing the openid identity scope (not because a data scope is malformed). If your error_description references openid and the userinfo endpoint, add openid to the scope parameter of your authorize request — it's free, always included in the default scope set, and required for any OIDC identity endpoint.
invalid_target
The resource parameter you sent on the token request is malformed. Per RFC 8707 §2, resource must be an absolute http(s) URI without query, fragment, or userinfo components, and ≤ 2048 characters.
invalid_token
The access token presented to a protected endpoint is invalid. Three sub-cases (the error_description discriminates):
- Token expired. Use your stored
refresh_tokento callPOST /api/oauth/tokenand obtain a newaccess_token. No user-facing re-authorization needed. - Token revoked. The user (or an admin) revoked this token. The user must restart the OAuth authorize flow.
- Token unknown / malformed. The bearer string is invalid, missing, or from a different issuer. Confirm your
Authorization: Bearer <access_token>header has the right value.
server_error
An unexpected condition prevented the authorization server from fulfilling the request. This is a Loopwise-side fault. Retry with backoff; if it persists, contact support with the response timestamp.
temporarily_unavailable
The authorization server is temporarily overloaded or under maintenance. Retry with backoff.
unauthorized_client
The client is not authorized to perform this request using this method. Confirm:
- The application audience (
staff/org_admin/member) at/oauth/applicationsmatches the flow you're running. - The grant type your call uses is enabled on the client.
unsupported_grant_type
The authorization server does not support the grant type you sent. Loopwise's third-party integration surface supports authorization_code and refresh_token. client_credentials is configured at the protocol level (Doorkeeper allows it in grant_flows) but is reserved for internal service-to-service callers — third-party OAuth clients should not rely on it. Other grant types (password, device_code) are not enabled at all.
unsupported_response_type
The authorization server does not support the response type you requested. Loopwise supports code only — token (implicit flow) and other response types are not enabled.
Related references
- Authorization endpoints — flow that emits
access_denied,invalid_redirect_uri,unsupported_response_type. - Token endpoints — flow that emits
invalid_grant,invalid_client,invalid_scope,invalid_target,invalid_token. - Scopes — when diagnosing
invalid_scope.