Loopwise Docs
Reference

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:

  1. The user clicked Deny on the consent screen. They must restart the authorize flow and click Allow.
  2. 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.
  3. A scope your application requests was removed from the client's allowed-scopes list between authorize and callback. Check /oauth/applications for the current allowed-scope set on this client.

invalid_client

Client authentication failed. Three branches:

  1. client_id not found. Confirm the value you're sending exactly matches what's shown at /oauth/applications — no trailing whitespace, no truncation.
  2. client_secret wrong. The client exists but the secret was rejected. Either your .env is out of date, or the secret was regenerated. Open the client at /oauth/applications to verify or regenerate.
  3. Audience mismatch. The client was found, but its audience (staff / org_admin / member) doesn't match the flow you're executing. A member-audience client cannot run a staff flow; a staff-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/token response returns a new refresh_token that 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 returns invalid_grant and 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:

  1. Open /oauth/applications, navigate to this OAuth client, and copy the exact entries in Redirect URIs.
  2. Compare to what you're sending: scheme (http vs https), 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:

  1. Fetch /.well-known/oauth-authorization-server and check scopes_supported — that's the universe of scopes a discovery-aware client can request.
  2. Open the client at /oauth/applications and check its allowed-scope list — the intersection with scopes_supported is 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_token to call POST /api/oauth/token and obtain a new access_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/applications matches 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.

  • 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.

On this page