Profiles·Public

@supabase/supabase-js

semver>=2.0.0 <3.0.0postconditions69functions28last verified2026-06-25coverage score100%

Postconditions: what we check

  • signUp · weak-password
    error
    WhenPassword does not meet strength requirements
    ThrowsAuthApiError with error.message about password strength
    Required handlingCaller MUST validate password strength before signup. Display user-friendly error with password requirements. DO NOT retry without user changing password.
    costhighin prodimmediate exceptionusers seelost datavisibilityvisible
    Sources[1]
  • signUp · user-already-exists
    error
    WhenEmail already registered
    ThrowsAuthApiError with error related to duplicate user
    Required handlingCaller MUST handle duplicate user gracefully. Provide clear error message to user. Consider redirecting to login page.
    costhighin prodimmediate exceptionusers seelost datavisibilityvisible
    Sources[1]
  • signUp · rate-limit-exceeded
    error
    WhenToo many signup attempts (429)
    ThrowsAuthApiError with status 429
    Required handlingCaller MUST handle rate limit errors gracefully. Implement exponential backoff. Show user-friendly message about trying again later. DO NOT automatically retry - may indicate abuse.
    costhighin prodimmediate exceptionusers seelost datavisibilityvisible
    Sources[2]
  • signUp · server-error
    error
    WhenAuth server degraded (500)
    ThrowsAuthApiError with status 500
    Required handling500 errors typically indicate issues with database or SMTP provider. Retry with exponential backoff. Check logs for database triggers or SMTP issues. Alert operations if persistent.
    costhighin prodimmediate exceptionusers seelost datavisibilityvisible
    Sources[3]
  • signInWithPassword · invalid-credentials
    error
    WhenEmail or password incorrect
    ThrowsAuthApiError with error about invalid credentials
    Required handlingCaller MUST handle invalid credentials gracefully. DO NOT specify whether email or password was wrong (security). Implement rate limiting to prevent brute force attacks. Consider account lockout after multiple failures.
    costhighin prodimmediate exceptionusers seelost datavisibilityvisible
    Sources[1]
  • signInWithPassword · rate-limit-exceeded
    error
    WhenToo many login attempts (429)
    ThrowsAuthApiError with status 429
    Required handlingHandle rate limit errors with user-friendly message. This often indicates potential abuse. Consider CAPTCHA or additional verification.
    costhighin prodimmediate exceptionusers seelost datavisibilityvisible
    Sources[1]
  • signInWithPassword · user-not-confirmed
    error
    WhenUser hasn't confirmed email
    ThrowsAuthApiError indicating email not confirmed
    Required handlingRedirect user to email confirmation flow. Provide option to resend confirmation email.
    costhighin prodimmediate exceptionusers seelost datavisibilityvisible
    Sources[2]
  • signInWithPassword · server-error
    error
    WhenAuth server error (500)
    ThrowsAuthApiError with status 500
    Required handlingRetry with exponential backoff. Check database and SMTP provider health.
    costhighin prodimmediate exceptionusers seelost datavisibilityvisible
    Sources[3]
  • from · table-not-found
    error
    WhenTable does not exist
    ThrowsError indicating table not found
    Required handlingVerify table name is correct. Ensure migrations have been applied.
    costhighin prodimmediate exceptionusers seelost datavisibilityvisible
    Sources[4]
  • select · rls-policy-violation
    error
    WhenRow Level Security policy denies access (42501)
    ThrowsError with code '42501' or status 403
    Required handlingCaller MUST handle RLS policy violations gracefully. This indicates user doesn't have permission to access data. CRITICAL: Ensure RLS policies are configured for all tables. Using anon or authenticated role without RLS is a MAJOR security risk. Return empty result or access denied message to user.
    costhighin prodimmediate exceptionusers seelost datavisibilityvisible
    Sources[5]
  • select · column-access-denied
    error
    WhenColumn-level RLS denies access to specific columns
    ThrowsError with code '42501' when using select *
    Required handlingAvoid using select * if column-level RLS is configured. Explicitly select only columns user should access. Handle 42501 errors by selecting subset of columns.
    costhighin prodimmediate exceptionusers seelost datavisibilityvisible
    Sources[5]
  • select · connection-error
    error
    WhenDatabase connection failed
    ThrowsNetwork or connection error
    Required handlingImplement retry with exponential backoff. Check database health status. Alert operations if persistent.
    costhighin prodimmediate exceptionusers seelost datavisibilityvisible
    Sources[6]
  • insert · rls-policy-violation
    error
    WhenRLS policy denies insert permission (42501)
    ThrowsError with code '42501'
    Required handlingEnsure RLS policies allow INSERT for user's role. Check if user is authenticated if policy requires it. Return clear error about insufficient permissions.
    costhighin prodimmediate exceptionusers seelost datavisibilityvisible
    Sources[5]
  • insert · unique-constraint-violation
    error
    WhenUnique constraint violated (23505)
    ThrowsError with code '23505'
    Required handlingHandle duplicate key violations gracefully. Check if record already exists before inserting. Return user-friendly error message.
    costhighin prodimmediate exceptionusers seelost datavisibilityvisible
    Sources[6]
  • insert · foreign-key-violation
    error
    WhenForeign key constraint violated (23503)
    ThrowsError with code '23503'
    Required handlingVerify referenced records exist before insert. DO NOT retry without fixing data integrity issue.
    costhighin prodimmediate exceptionusers seelost datavisibilityvisible
    Sources[6]
  • insert · connection-error
    error
    WhenDatabase connection failed
    ThrowsNetwork error
    Required handlingRetry with exponential backoff
    costhighin prodimmediate exceptionusers seelost datavisibilityvisible
    Sources[6]
  • update · rls-policy-violation
    error
    WhenRLS policy denies update permission (42501)
    ThrowsError with code '42501'
    Required handlingEnsure RLS policies allow UPDATE for user. Verify user owns the record if policy checks ownership. Return insufficient permissions error.
    costhighin prodimmediate exceptionusers seelost datavisibilityvisible
    Sources[5]
  • update · record-not-found
    warning
    WhenNo records match the update criteria
    ReturnsEmpty data array
    Required handlingCheck if returned data array is empty. This may indicate record doesn't exist or RLS filters it out. Distinguish between "not found" and "no permission".
    costhighin prodimmediate exceptionusers seelost datavisibilityvisible
    Sources[6]
  • update · unique-constraint-violation
    error
    WhenUpdate would violate unique constraint (23505)
    ThrowsError with code '23505'
    Required handlingCheck for conflicts before updating. DO NOT retry.
    costhighin prodimmediate exceptionusers seelost datavisibilityvisible
    Sources[6]
  • delete · rls-policy-violation
    error
    WhenRLS policy denies delete permission (42501)
    ThrowsError with code '42501'
    Required handlingEnsure RLS policies allow DELETE for user. Verify user owns the record if policy checks ownership.
    costhighin prodimmediate exceptionusers seelost datavisibilityvisible
    Sources[5]
  • delete · foreign-key-violation
    error
    WhenCannot delete due to dependent records (23503)
    ThrowsError with code '23503'
    Required handlingDelete dependent records first or use CASCADE. Return error about dependent records to user.
    costhighin prodimmediate exceptionusers seelost datavisibilityvisible
    Sources[6]
  • rpc · function-not-found
    error
    WhenPostgreSQL function does not exist (42883)
    ThrowsError with code '42883'
    Required handlingVerify function name is correct. Ensure function is created in database. DO NOT retry without fixing function name.
    costhighin prodimmediate exceptionusers seelost datavisibilityvisible
    Sources[6]
  • rpc · permission-denied
    error
    WhenUser doesn't have permission to execute function (42501)
    ThrowsError with code '42501'
    Required handlingGrant EXECUTE permission on function to appropriate role. Check if user is authenticated if required.
    costhighin prodimmediate exceptionusers seelost datavisibilityvisible
    Sources[5]
  • rpc · rpc-error
    error
    WhenFunction execution error
    ThrowsError from function execution
    Required handlingCheck function logs for specific error. Handle business logic errors from function. May include constraint violations, custom errors, etc.
    costhighin prodimmediate exceptionusers seelost datavisibilityvisible
    Sources[6]
  • signOut · signout-session-missing
    warning
    WhenNo active session exists when signOut() is called
    ThrowsAuthSessionMissingError (name='AuthSessionMissingError', status=400)
    Required handlingCaller MUST check the returned { error } object. signOut() does NOT throw — it returns { error }. A missing session error on logout is usually safe to ignore (user is already effectively signed out), but must be explicitly handled to avoid silent failures in session cleanup callbacks. Log the error and proceed with local UI cleanup.
    costlowin prodsilent failureusers seedegraded performancevisibilitysilent
    Sources[7]
  • signOut · signout-network-error
    warning
    WhenNetwork request to Supabase Auth fails (offline, DNS failure, timeout)
    ThrowsAuthRetryableFetchError (name='AuthRetryableFetchError') or AuthError with network cause
    Required handlingCaller MUST check { error } on the signOut() response. On network failure, clear the local session regardless (local cleanup is safe). Inform the user that remote session revocation may not have succeeded. The JWT remains valid until it expires — this is a known Supabase limitation.
    costmediumin prodsilent failureusers seedegraded performancevisibilitysilent
    Sources[7]
  • signInWithOAuth · oauth-provider-not-supported
    error
    WhenThe specified OAuth provider is not enabled in Supabase project settings
    ThrowsAuthApiError with error.code='oauth_provider_not_supported'
    Required handlingCaller MUST check { error } in the response. Display a user-friendly message that this login method is unavailable. DO NOT retry automatically — this is a configuration error, not a transient failure.
    costhighin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[8]
  • signInWithOAuth · oauth-pkce-code-verifier-missing
    error
    WhenPKCE flow: callback URL reached but code_verifier is missing from storage. Happens when auth was initiated in a different browser/tab, user cleared storage, or SSR framework stored the verifier incorrectly.
    ThrowsAuthPKCECodeVerifierMissingError (name='AuthPKCECodeVerifierMissingError', status=400, code='pkce_code_verifier_not_found')
    Required handlingCaller MUST check { error } in the exchangeCodeForSession() response. Redirect user to the login page with a message to try again from the same browser. For SSR frameworks (Next.js, SvelteKit), use @supabase/ssr to store the verifier in cookies rather than localStorage to avoid cross-context failures.
    costhighin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[8]
  • signInWithOAuth · oauth-bad-callback-state
    error
    WhenOAuth state parameter mismatch (CSRF protection failure or replay attack)
    ThrowsAuthApiError with error.code='bad_oauth_state' or 'bad_oauth_callback'
    Required handlingCaller MUST check { error } in the auth callback handler. Redirect to login page — do not retry the OAuth flow automatically. Log the state mismatch for security review. This can be a legitimate security alert if the state was tampered with.
    costhighin prodsilent failureusers seeservice unavailablevisibilitysilent
    Sources[8]
  • getSession · session-refresh-token-expired
    error
    WhenRefresh token is expired or has already been used (rotated). Common after long periods of inactivity or if the same refresh token is used twice.
    ThrowsAuthApiError with error.code='refresh_token_not_found' or 'refresh_token_already_used' or 'session_expired'
    Required handlingCaller MUST check { error } in the getSession() response. Redirect user to the login page — session cannot be recovered without re-auth. Clear any locally cached session state. DO NOT retry getSession() — the token is permanently invalid.
    costhighin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[9]
  • getSession · session-network-error
    warning
    WhenNetwork failure prevents session refresh (offline, Supabase degraded)
    ThrowsAuthRetryableFetchError (name='AuthRetryableFetchError')
    Required handlingCaller MUST check { error } on the getSession() response. AuthRetryableFetchError indicates a transient failure — safe to retry with backoff. Show the user an offline/degraded state indicator rather than silently failing. Do NOT treat a network error as "no session" — the session may still be valid.
    costmediumin prodsilent failureusers seedegraded performancevisibilitysilent
    Sources[9]
  • resetPasswordForEmail · reset-password-rate-limit
    error
    WhenToo many password reset emails sent to this address within the rate limit window
    ThrowsAuthApiError with error.code='over_email_send_rate_limit' and error.status=429
    Required handlingCaller MUST check { error } and inspect error.code. DO NOT retry automatically — this makes brute-force abuse worse. Show user a message: "Check your inbox. You can request another email in X minutes." Implement client-side throttling to prevent accidental repeated submissions.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[10]
  • resetPasswordForEmail · reset-password-email-not-authorized
    warning
    WhenEmail address is not on the project's allowed list (when allow list is configured)
    ThrowsAuthApiError with error.code='email_address_not_authorized'
    Required handlingCaller MUST check { error.code } and provide a user-facing message. This error indicates the email is blocked by project configuration, not a user mistake. Log for admin review — may indicate a configuration gap.
    costlowin prodsilent failureusers seedegraded performancevisibilitysilent
    Sources[10]
  • storage.upload · storage-upload-bucket-not-found
    error
    WhenThe specified bucket does not exist
    ThrowsStorageApiError with error.status=400 or 404 and error.message containing 'Bucket not found'
    Required handlingCaller MUST check { error } in the upload() response. Verify bucket name in the Supabase dashboard. DO NOT retry with the same bucket name — this is a configuration error. Return a clear error to the user; do not expose bucket names in user-facing messages.
    costhighin prodsilent failureusers seelost transactionvisibilitysilent
    Sources[11]
  • storage.upload · storage-upload-unauthorized
    error
    WhenRLS policy denies upload (user not authenticated or bucket policy blocks write)
    ThrowsStorageApiError with error.status=400 and error.statusCode containing 'Unauthorized' or RLS policy error
    Required handlingCaller MUST check { error } in the upload() response. Verify the user is authenticated before attempting upload. Check bucket RLS policies in Storage settings — public buckets allow unauthenticated upload, private buckets require a valid session. Return a user-facing error: "You must be logged in to upload files."
    costhighin prodsilent failureusers seelost transactionvisibilitysilent
    Sources[11]
  • storage.upload · storage-upload-duplicate-file
    warning
    WhenFile already exists at this path and upsert is false (default)
    ThrowsStorageApiError with error.status=409 (Conflict)
    Required handlingCaller MUST check { error } in the upload() response. To overwrite, pass { upsert: true } in fileOptions. To avoid conflicts, generate unique paths (UUID prefix) or check for existence first. Return a user-facing message: "A file with this name already exists."
    costmediumin prodsilent failureusers seelost transactionvisibilitysilent
    Sources[11]
  • storage.upload · storage-upload-size-limit
    error
    WhenFile exceeds the bucket's maximum file size limit
    ThrowsStorageApiError with error.status=413 (Payload Too Large)
    Required handlingCaller MUST check { error } in the upload() response. Validate file size client-side before upload to provide immediate feedback. Return a clear user message: "File too large. Maximum size is X MB." DO NOT retry with the same file.
    costmediumin prodsilent failureusers seelost transactionvisibilitysilent
    Sources[11]
  • functions.invoke · functions-invoke-http-error
    error
    WhenEdge function executed but returned a non-2xx HTTP status (4xx or 5xx)
    ThrowsFunctionsHttpError (name='FunctionsHttpError', extends FunctionsError) — error in { error } field
    Required handlingCaller MUST check { error } and use instanceof to distinguish error types: if (error instanceof FunctionsHttpError) — function ran but returned error response if (error instanceof FunctionsRelayError) — Supabase relay failed if (error instanceof FunctionsFetchError) — network request failed For FunctionsHttpError, read the response body: await error.context.json() Distinguishing error types is critical for correct retry logic.
    costhighin prodsilent failureusers seelost transactionvisibilitysilent
    Sources[12]
  • functions.invoke · functions-invoke-relay-error
    error
    WhenSupabase relay infrastructure cannot reach the edge function (relay-side failure)
    ThrowsFunctionsRelayError (name='FunctionsRelayError', x-relay-error header='true') — error in { error } field
    Required handlingCaller MUST distinguish FunctionsRelayError from FunctionsHttpError. FunctionsRelayError is a Supabase infrastructure problem, NOT a function logic problem. These are transient and safe to retry with exponential backoff. Alert ops if relay errors persist — may indicate regional outage.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[12]
  • functions.invoke · functions-invoke-fetch-error
    warning
    WhenNetwork request to edge function failed completely (offline, DNS, timeout)
    ThrowsFunctionsFetchError (name='FunctionsFetchError') — wraps the original fetch error in error.context
    Required handlingCaller MUST handle FunctionsFetchError as a network-layer failure. The function was never reached — no side effects occurred on the function side. Safe to retry. Implement exponential backoff with a reasonable timeout (30s). Show user a "connection error, retrying..." message for user-initiated operations.
    costhighin prodsilent failureusers seelost transactionvisibilitysilent
    Sources[12]
  • signInWithOtp · signin-otp-rate-limit
    error
    WhenToo many magic link / OTP requests for this email or phone (429)
    ThrowsAuthApiError with error.code='over_email_send_rate_limit' or 'over_sms_send_rate_limit' and error.status=429
    Required handlingCaller MUST check { error } and inspect error.code. DO NOT retry automatically — repeated requests amplify abuse risk and SMS spend. Show user a clear "Check your inbox/SMS. You can request another link in N minutes." Implement client-side debouncing on the submit button to prevent double-fires.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[13]
  • signInWithOtp · signin-otp-signup-disabled
    error
    WhenshouldCreateUser is true (default) but the project has signups disabled. User does not exist and cannot be created via passwordless flow.
    ThrowsAuthApiError with error.code='signup_disabled' or 'otp_disabled'
    Required handlingCaller MUST check { error.code } before treating the flow as successful. Redirect the user to a sign-up alternative or show "passwordless login is disabled." DO NOT silently swallow this error — user will wait for an email that never arrives, producing a "did the email get sent?" support ticket.
    costmediumin prodsilent failureusers seeservice unavailablevisibilitysilent
    Sources[13]
  • signInWithOtp · signin-otp-network-error
    warning
    WhenNetwork failure prevents the OTP request from reaching Supabase Auth
    ThrowsAuthRetryableFetchError (name='AuthRetryableFetchError')
    Required handlingCaller MUST check { error } on the signInWithOtp response. Show an offline indicator and prompt the user to retry — do NOT auto-retry on a hidden timer (it produces duplicate emails when the network recovers).
    costmediumin prodsilent failureusers seedegraded performancevisibilitysilent
    Sources[13]
  • verifyOtp · verify-otp-token-expired
    error
    WhenThe magic link or OTP code has expired (default 1 hour for magic links, shorter for SMS)
    ThrowsAuthApiError with error.code='otp_expired' or 'token_expired' and error.status=401 or 403
    Required handlingCaller MUST check { error.code } and surface a clear "code expired" message. Offer the user a button to request a new code via signInWithOtp(). DO NOT auto-trigger a resend — user may have already triggered a second send and is mid-keystroke on the new code.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[14]
  • verifyOtp · verify-otp-invalid-code
    error
    WhenThe OTP code does not match what was sent (user typo or replay attack)
    ThrowsAuthApiError with error.code='otp_invalid' or 'validation_failed' and error.status=401 or 403
    Required handlingCaller MUST check { error.code } and increment a client-side attempt counter. After N failed attempts (typically 3-5), lock the input and require a resend via signInWithOtp() — protects against brute-force SMS code guessing. Log the failure for security review if attempts cluster from one IP.
    costhighin prodsilent failureusers seeservice unavailablevisibilitysilent
    Sources[14]
  • getUser · get-user-session-expired
    error
    WhenThe user's session has expired or the JWT is no longer valid (revoked, rotated past, or the project's JWT secret was rotated). On the server, this is the canonical signal that the request is unauthenticated.
    ThrowsAuthApiError with error.code='session_expired', 'bad_jwt', or 'user_not_found' and error.status=401
    Required handlingCaller MUST check { error } before treating the user as authenticated. On the server (Next.js Route Handler / middleware / SvelteKit hooks), treat a 401 from getUser() as "not signed in" and return a 401 response or redirect to login. DO NOT trust getSession() result without getUser() validation — the session may be stale. On the client, redirect to /login and clear local auth state.
    costcriticalin prodsilent failureusers seeservice unavailablevisibilitysilent
    Sources[15][16]
  • getUser · get-user-network-error
    error
    WhenNetwork failure prevents JWT validation with the auth server
    ThrowsAuthRetryableFetchError (name='AuthRetryableFetchError')
    Required handlingCaller MUST distinguish AuthRetryableFetchError from a real auth failure. A network error is NOT the same as "user is not authenticated" — treating it as such logs users out during transient outages. On the client, retry with exponential backoff (2-3 attempts, jittered). On the server (SSR/middleware), return a 503 to the user rather than 401 — instructing the browser the user state is unknown, not invalid.
    costhighin prodsilent failureusers seeservice unavailablevisibilitysilent
    Sources[15]
  • updateUser · update-user-weak-password
    error
    WhenNew password does not meet the project's password strength requirements
    ThrowsAuthApiError with error.code='weak_password' and error.status=422
    Required handlingCaller MUST check { error.code } and surface the password requirements UI. Inspect error.weakPassword.reasons (array of strings) to show specific missing criteria — e.g. ['length', 'characters']. DO NOT submit the form again with the same password — it will fail identically.
    costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[17]
  • updateUser · update-user-reauthentication-needed
    error
    WhenProject has "Secure password change" enabled and the user's session is older than the reauthentication window (typically 24h). updateUser({ password }) requires calling reauthenticate() first to receive a nonce, then passing { password, nonce } to updateUser().
    ThrowsAuthApiError with error.code='reauthentication_needed'
    Required handlingCaller MUST check { error.code === 'reauthentication_needed' } and trigger the reauthentication flow: 1. Call supabase.auth.reauthenticate() — sends nonce to user's email/phone 2. Collect the nonce from the user 3. Re-submit updateUser({ password, nonce }) DO NOT silently fail the password update — the user thinks the change succeeded and continues using the old password until the next login fails.
    costhighin prodsilent failureusers seelost transactionvisibilitysilent
    Sources[17]
  • updateUser · update-user-same-password
    warning
    WhenNew password is identical to the user's current password (when same-password rejection is enabled)
    ThrowsAuthApiError with error.code='same_password'
    Required handlingCaller MUST check { error.code } and show a clear "choose a different password" message. Do NOT auto-retry — there is no transient path to recovery.
    costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[17]
  • refreshSession · refresh-session-token-revoked
    error
    WhenThe refresh token has already been used (rotation collision), is unknown to the auth server (logged out from another tab), or the user was admin-deleted.
    ThrowsAuthApiError with error.code='refresh_token_not_found' or 'refresh_token_already_used' or 'session_not_found' and error.status=401
    Required handlingCaller MUST check { error } and treat this as a hard sign-out signal. Clear local session state, redirect to /login. DO NOT retry refreshSession() — the refresh token is permanently invalid (single-use semantics with rotation). If the error occurs mid-operation (e.g. before an upload), abort the operation and instruct the user to sign in again before resuming.
    costhighin prodsilent failureusers seelost transactionvisibilitysilent
    Sources[18]
  • refreshSession · refresh-session-network-error
    warning
    WhenNetwork failure prevents the refresh request from reaching Supabase Auth
    ThrowsAuthRetryableFetchError (name='AuthRetryableFetchError')
    Required handlingCaller MUST distinguish AuthRetryableFetchError from token revocation — a network error is transient and safe to retry with exponential backoff. DO NOT log the user out on a single network failure; the existing session may still be valid for the remaining lifetime of the access token. Surface "offline — retrying" indicator to the user rather than redirecting.
    costmediumin prodsilent failureusers seedegraded performancevisibilitysilent
    Sources[18]
  • exchangeCodeForSession · exchange-code-pkce-verifier-missing
    error
    WhenThe auth callback was reached but the PKCE code verifier was lost from storage. Common causes: user clicked the magic link in a different browser, cleared cookies/localStorage mid-flow, or the SSR framework didn't persist the verifier across the redirect (e.g. cookies set with insufficient SameSite).
    ThrowsAuthPKCECodeVerifierMissingError (name='AuthPKCECodeVerifierMissingError', error.code='pkce_code_verifier_not_found') or AuthApiError with error.code='bad_code_verifier'
    Required handlingCaller MUST check { error } in the route handler before issuing a session cookie or redirecting. Redirect the user back to /login with a message: "Open this link in the same browser where you started signing in." For Next.js / SvelteKit auth callbacks, ensure verifier storage uses cookies via @supabase/ssr (not localStorage) so the verifier survives the redirect from the email client.
    costhighin prodimmediate exceptionusers seeservice unavailablevisibilityvisible
    Sources[19][20]
  • exchangeCodeForSession · exchange-code-expired
    error
    WhenThe authorization code is expired or has already been redeemed (single-use semantics). Magic link codes typically expire after 1 hour.
    ThrowsAuthApiError with error.code='otp_expired' or 'flow_state_expired' and error.status=401 or 403
    Required handlingCaller MUST check { error } in the callback handler before issuing a session. Redirect to /login with a clear "this link expired, request a new one" message. DO NOT silently render the post-login page — user will appear signed-in client-side but every API call will 401, producing a confusing broken state.
    costmediumin prodsilent failureusers seeservice unavailablevisibilitysilent
    Sources[19]
  • storage.download · storage-download-not-found
    error
    WhenThe file at the specified path does not exist in the bucket
    ThrowsStorageApiError with error.status=404 and error.message containing 'Object not found' or 'not_found'
    Required handlingCaller MUST check { error } before treating data as a valid file blob. A null data with no error is also possible — handle both cases. Show user a clear "file not found" message. DO NOT retry — the file will not appear by retrying. For user-uploaded content, verify the database row still references this path (file rows can outlive deleted storage objects).
    costmediumin prodsilent failureusers seelost datavisibilitysilent
    Sources[21]
  • storage.download · storage-download-unauthorized
    error
    WhenRLS policy denies read access — user is unauthenticated and bucket is private, or user lacks the SELECT policy for this object path.
    ThrowsStorageApiError with error.status=400 or 403 and error.statusCode containing 'Unauthorized' or RLS-policy text
    Required handlingCaller MUST check { error } and distinguish 'not found' from 'unauthorized'. DO NOT leak the difference to end users — a 'file not found' message for both prevents enumeration of private file paths. Log the violation server-side for security review if it's unexpected.
    costhighin prodsilent failureusers seeservice unavailablevisibilitysilent
    Sources[21]
  • storage.createSignedUrl · storage-signed-url-not-found
    error
    WhenThe file at the specified path does not exist when generating the signed URL
    ThrowsStorageApiError with error.status=400 or 404 and error.message containing 'Object not found'
    Required handlingCaller MUST check { error } before sharing the signed URL with the requester. DO NOT return a signed URL pointing at a missing object — the recipient gets a confusing 404 at use-time with no actionable error. Surface the missing-file error to the requester immediately so they can re-upload or pick a different file.
    costmediumin prodsilent failureusers seelost transactionvisibilitysilent
    Sources[22]
  • storage.createSignedUrl · storage-signed-url-unauthorized
    error
    WhenThe current client lacks permission to sign URLs for this path. Anon clients cannot sign URLs for private bucket objects; only the service role and authenticated users with appropriate RLS policies can.
    ThrowsStorageApiError with error.status=400 or 403 and error.statusCode containing 'Unauthorized'
    Required handlingCaller MUST check { error } before returning the signed URL. Move signed-URL generation to a server-side route (Edge Function, Next.js Route Handler) where you can use the service role key safely — generating on the client with the anon key fails for any private bucket object. DO NOT expose the service role key to the browser to "work around" this.
    costhighin prodsilent failureusers seeservice unavailablevisibilitysilent
    Sources[22]
  • auth.getUserIdentities · auth-get-identities-no-session
    error
    WhenThe user has no active session or the session has expired. getUserIdentities() delegates to getUser() which returns AuthApiError when no valid session exists.
    ThrowsAuthApiError with error.message containing 'User not found' or 'Auth session missing' or error.code containing 'session'
    Required handlingCaller MUST check { error } before accessing data.identities. Redirect to login when session is missing. DO NOT treat an empty identities list and an auth error as equivalent — an error means the request failed, while an empty list is valid for a password-only account.
    costlowin prodsilent failureusers seeservice unavailablevisibilitysilent
    Sources[23][1]
  • auth.linkIdentity · auth-link-identity-already-exists
    error
    WhenThe OAuth provider account being linked is already associated with this user or another Supabase user. The server returns error code 'identity_already_exists'.
    ThrowsAuthApiError with error.code === 'identity_already_exists'
    Required handlingCaller MUST check { error } and handle identity_already_exists explicitly. Display a message that this provider account is already linked. DO NOT retry silently — the error is deterministic and retrying will return the same result.
    costlowin prodsilent failureusers seeauthentication failurevisibilitysilent
    Sources[24][1]
  • auth.linkIdentity · auth-link-identity-manual-linking-disabled
    error
    WhenThe "Enable Manual Linking" setting is turned OFF in the Supabase Auth project configuration. Calling linkIdentity() when this feature is disabled returns error code 'manual_linking_disabled'.
    ThrowsAuthApiError with error.code === 'manual_linking_disabled'
    Required handlingCaller MUST check { error } for manual_linking_disabled and surface a clear message to users. DO NOT expose the config setting name — tell users "account linking is not available." Fix by enabling Manual Linking in the Supabase Dashboard under Auth > Settings > User Management > Manual Linking.
    costlowin prodsilent failureusers seeservice unavailablevisibilitysilent
    Sources[24][1]
  • auth.unlinkIdentity · auth-unlink-identity-single-identity
    error
    WhenThe user has only one linked identity and no password set. Unlinking it would make the account permanently inaccessible. The server returns error code 'single_identity_not_deletable' to prevent lockout.
    ThrowsAuthApiError with error.code === 'single_identity_not_deletable'
    Required handlingCaller MUST check { error } for single_identity_not_deletable BEFORE completing the unlink flow. This is a DATA LOSS scenario — the user would be permanently locked out of their account. Guard the unlink UI by checking getUserIdentities() first: if the user has exactly one identity and no password, disable the unlink button and explain they must set a password first.
    costhighin prodimmediate exceptionusers seeauthentication failurevisibilityvisible
    Sources[25][1]
  • auth.unlinkIdentity · auth-unlink-identity-email-conflict
    error
    WhenUnlinking this identity would cause an email conflict — for example, the email from the OAuth provider is the same as another account's email and Supabase prevents the conflict. Returns error code 'email_conflict_identity_not_deletable'.
    ThrowsAuthApiError with error.code === 'email_conflict_identity_not_deletable'
    Required handlingCaller MUST check { error } and present a clear explanation to the user. DO NOT retry automatically. Advise the user to contact support or change their email before unlinking.
    costmediumin prodsilent failureusers seeauthentication failurevisibilitysilent
    Sources[25][1]
  • storage.update · storage-update-not-found
    error
    WhenThe file at the specified path does not exist in the bucket. Unlike upload() which creates new files, update() requires the object to already exist and returns an error if it is missing.
    ThrowsStorageApiError with error.status=400 or 404 and error.message containing 'Object not found'
    Required handlingCaller MUST check { error } before treating the update as successful. Distinguish between 'not found' (use upload() instead) and other errors. DO NOT silently fall back to upload() without explicit caller intent — this can create unintended duplicate objects in some configurations.
    costmediumin prodsilent failureusers seelost datavisibilitysilent
    Sources[26]
  • storage.update · storage-update-unauthorized
    error
    WhenThe current user lacks UPDATE permission on this object path. RLS policy denies the PUT operation — user is not authenticated or lacks the UPDATE RLS policy for this bucket path.
    ThrowsStorageApiError with error.status=400 or 403 and error.statusCode containing 'Unauthorized'
    Required handlingCaller MUST check { error } and surface a permission error to the user. Ensure the RLS policy includes UPDATE for authenticated users on this bucket. DO NOT expose internal bucket policy details in the error message.
    costhighin prodsilent failureusers seeservice unavailablevisibilitysilent
    Sources[26]
  • storage.createSignedUploadUrl · storage-create-signed-upload-url-no-token
    error
    WhenThe Supabase Storage server returned a response without a token field — typically caused by a misconfigured storage backend or an unexpected API response format. The client throws StorageError("No token returned by API").
    ThrowsStorageApiError with error.message === 'No token returned by API'
    Required handlingCaller MUST check { error } before returning the signed URL to the client. This error indicates a server-side misconfiguration, not a user error. Log it as a server error and return a 500 to the calling client. DO NOT pass a null/undefined signedUrl to the uploader — it will produce a confusing network error at upload time instead of a clear failure.
    costmediumin prodsilent failureusers seelost transactionvisibilitysilent
    Sources[27]
  • storage.createSignedUploadUrl · storage-create-signed-upload-url-unauthorized
    error
    WhenThe calling user lacks INSERT permission on the storage.objects table for this bucket path. RLS policy blocks the signed URL generation — only authenticated users with the correct INSERT policy can create upload URLs.
    ThrowsStorageApiError with error.status=400 or 403 and error.statusCode containing 'Unauthorized'
    Required handlingCaller MUST check { error } and surface a clear permission message. Move signed upload URL generation to a server-side route where you can verify the user's identity and apply fine-grained access checks before issuing the upload token. DO NOT generate upload URLs client-side with the service role key.
    costhighin prodsilent failureusers seeservice unavailablevisibilitysilent
    Sources[27]
  • storage.uploadToSignedUrl · storage-upload-to-signed-url-expired-token
    error
    WhenThe signed upload token has expired (valid for 2 hours after creation) or the token is invalid/malformed. The storage server rejects the upload and returns a 400 error.
    ThrowsStorageApiError with error.status=400 and error.message containing 'Invalid token' or 'Token expired'
    Required handlingCaller MUST check { error } and detect token expiry. If the token is expired, request a new signed URL from the server (call createSignedUploadUrl again) and retry the upload. DO NOT reuse an expired token — it will always fail. Show a user-friendly "upload link expired, please try again" message rather than a raw error.
    costmediumin prodsilent failureusers seelost transactionvisibilitysilent
    Sources[28]
  • storage.uploadToSignedUrl · storage-upload-to-signed-url-unauthorized
    error
    WhenThe upload is rejected because the token does not grant permission for this specific path, the bucket's RLS INSERT policy is not met, or the token was issued for a different path than the one being uploaded to.
    ThrowsStorageApiError with error.status=400 or 403 and error.statusCode containing 'Unauthorized'
    Required handlingCaller MUST check { error } and surface an access-denied message. Ensure the token path matches the upload path exactly — tokens are path-scoped. DO NOT silently ignore this error; the file was NOT uploaded.
    costhighin prodsilent failureusers seeservice unavailablevisibilitysilent
    Sources[28]

Sources

Every postcondition cites at least one of these. Grouped by source type; numbered to match the footnotes above.

Official documentation

Research notes

Curator notes from SOURCES.md captured when the profile was written so you can verify the reasoning, not just the rules.

Supabase Nark profile - Sources

Official Documentation

Supabase Client Documentation

Error Handling Guides

Security Best Practices

Error Types

Auth Errors

Supabase Auth errors are categorized into two main types:

  1. AuthApiError: Errors originating from the Supabase Auth API
  2. CustomAuthError: Errors originating from the client library's state

Critical Best Practice: Use isAuthApiError instead of instanceof checks.

import { isAuthApiError } from '@supabase/supabase-js';

try {
  const { data, error } = await supabase.auth.signIn({ email, password });
  if (error) throw error;
} catch (error) {
  if (isAuthApiError(error)) {
    // Handle API error
    console.error('Auth API error:', error.code, error.message);
  } else {
    // Handle client error
    console.error('Client error:', error.name, error.message);
  }
}

Always check error.code and error.name, not string matching on error.message.

Source: https://supabase.com/docs/guides/auth/debugging/error-codes

Common HTTP Status Codes

429 - Rate Limit Exceeded

  • Occurs frequently in auth flows (signup, login, password reset)
  • MUST handle gracefully with user-friendly messaging
  • Implement exponential backoff
  • Consider CAPTCHA for repeated failures

500 - Internal Server Error

  • Usually indicates issues with database or SMTP provider
  • NOT an Auth service issue - external dependency failure
  • Check logs for database triggers or email delivery issues
  • Implement retry with exponential backoff

403 / 42501 - Permission Denied

  • Row Level Security (RLS) policy violation
  • User doesn't have permission to access data
  • Most common cause: RLS policies not configured or too restrictive

Source: https://supabase.com/docs/guides/troubleshooting/http-status-codes

Database Error Codes

Supabase uses PostgreSQL error codes (SQLSTATE):

42501 - Insufficient Privilege

  • RLS policy denies access (SELECT, INSERT, UPDATE, DELETE)
  • Column-level RLS denies access to specific columns
  • User doesn't have EXECUTE permission on RPC function

23505 - Unique Violation

  • Duplicate key constraint violation
  • Check for existing records before inserting

23503 - Foreign Key Violation

  • Referenced record doesn't exist
  • Verify foreign key references before operations

42883 - Undefined Function

  • PostgreSQL function (RPC) doesn't exist
  • Check function name spelling

42P01 - Undefined Table

  • Table doesn't exist
  • May indicate schema mismatch or missing migration

Source: https://www.postgresql.org/docs/current/errcodes-appendix.html

Row Level Security (RLS)

Critical Security Requirement

RLS is NOT enabled by default on new tables. This is the #1 security misconfiguration in Supabase applications.

ALWAYS:

  1. Enable RLS on every table: ALTER TABLE table_name ENABLE ROW LEVEL SECURITY;
  2. Create policies for each operation (SELECT, INSERT, UPDATE, DELETE)
  3. Test policies with different user roles

Example Policy:

-- Users can only see their own data
CREATE POLICY "Users can view own data"
  ON users
  FOR SELECT
  USING (auth.uid() = id);

Source: https://supabase.com/docs/guides/database/postgres/row-level-security

Common RLS Issues

Issue 1: Missing RLS Policies

  • Symptom: 42501 error or no data returned
  • Cause: RLS enabled but no policies created
  • Solution: Create policies for each role (anon, authenticated, service_role)

Issue 2: Service Role Bypassing RLS

  • Symptom: Expected RLS errors not occurring
  • Cause: Client initialized with service_role key
  • Solution: NEVER use service_role key in client-side code
  • Note: service_role ALWAYS bypasses RLS by design

Source: https://supabase.com/docs/guides/troubleshooting/why-is-my-service-role-key-client-getting-rls-errors-or-not-returning-data-7_1K9z

Issue 3: Column-Level RLS

  • Symptom: 42501 error when using select *
  • Cause: RLS restricts access to some columns
  • Solution: Select only accessible columns explicitly

Issue 4: SELECT Policy on INSERT/UPDATE

  • Symptom: 42501 error after successful insert/update
  • Cause: No SELECT policy to return inserted/updated data
  • Solutions:
    1. Add SELECT policy
    2. Use returning: 'minimal' option

Source: https://supabase.com/docs/guides/troubleshooting/why-is-my-service-role-key-client-getting-rls-errors-or-not-returning-data-7_1K9z

RLS Performance

Best Practices:

  • Keep policies simple
  • Avoid complex joins in policies
  • Use indexed columns in policies
  • Test policy performance with EXPLAIN ANALYZE
  • Consider SECURITY DEFINER functions for complex logic

Source: https://supabase.com/docs/guides/troubleshooting/rls-performance-and-best-practices-Z5Jjwv

Common Production Issues

Issue 1: Missing RLS Policies (CRITICAL)

Severity: Critical - Data exposure risk

Problem: RLS not enabled or policies missing, exposing all data

Statistics:

  • 170+ applications exposed in 2025 due to missing RLS
  • Thousands of misconfigured Supabase instances globally
  • #1 cause of Supabase data breaches

Solutions:

  1. Enable RLS on ALL tables
  2. Create policies for every operation
  3. Use security checklist before deployment
  4. Audit RLS configuration regularly

Sources:

Issue 2: Service Role Key in Client Code

Severity: Critical - Complete security bypass

Problem: Using service_role key in client-side JavaScript

Impact:

  • Bypasses ALL RLS policies
  • Full database access from client
  • Equivalent to giving users superuser access

Solution:

  • ONLY use service_role on server-side (API routes, serverless functions)
  • Use anon key for client-side code
  • Implement proper RLS policies for anon/authenticated roles

Source: https://supabase.com/docs/guides/troubleshooting/why-is-my-service-role-key-client-getting-rls-errors-or-not-returning-data-7_1K9z

Issue 3: Auth Rate Limiting (429 Errors)

Severity: High - Service degradation

Problem: Hitting auth rate limits during login/signup

Causes:

  • Brute force attempts
  • Excessive retry logic
  • Missing client-side validation
  • Automated testing without mocking

Solutions:

  1. Implement client-side validation before API calls
  2. Add CAPTCHA for repeated failures
  3. Use exponential backoff for retries
  4. Mock auth in tests, don't hit real API

Source: https://supabase.com/docs/guides/auth/troubleshooting

Issue 4: Auth 500 Errors

Severity: High - Authentication failures

Problem: 500 errors during signup/login

Common Causes:

  1. Database triggers failing: Error in custom trigger logic
  2. SMTP provider issues: Email delivery failures (look for "gomail" in logs)
  3. Database connection issues: Pool exhaustion or connectivity

Solutions:

  • Check database logs for trigger errors
  • Verify SMTP configuration
  • Test triggers in isolation
  • Monitor database health

Source: https://supabase.com/docs/guides/troubleshooting/resolving-500-status-authentication-errors-7bU5U8

Issue 5: Not Checking Error.code

Severity: Medium - Poor error handling

Problem: String matching on error messages instead of using error codes

Example of bad practice:

// ❌ WRONG - fragile, message may change
if (error.message.includes('already exists')) {
  // Handle duplicate
}

Correct approach:

// ✅ CORRECT - stable, documented codes
if (error.code === '23505') {
  // Handle unique violation
}

Source: https://supabase.com/docs/guides/auth/debugging/error-codes

Security Advisories

Last Checked: 2026-02-24

Major CVEs and Vulnerabilities

CVE-2025-48757 - Lovable AI Generator Missing RLS

  • Severity: Critical
  • Impact: 170+ applications affected, 13,000 users exposed in one leak
  • Cause: AI-generated code didn't include RLS policies
  • Lesson: Always audit generated code for security

Source: Supabase Security Flaw: 170+ Apps Exposed

Supabase MCP Vulnerability (July 2025)

  • Severity: Critical
  • Impact: AI coding assistants with service_role can bypass RLS via prompt injection
  • Attack: Embedded instructions in prompts leak sensitive data
  • Mitigation: Never give AI assistants service_role access

Source: Supabase MCP Leak

CVE-2025-57164 - Flowise RCE

  • Severity: Critical
  • Impact: Remote code execution via unsanitized "Supabase RPC Filter" field
  • Affected: Flowise through v3.0.4
  • Discovered: September 2025

Source: CVE-2025-57164

Systemic Configuration Issues (2025-2026)

Major audit findings:

  • Hundreds to thousands of misconfigured instances globally
  • Missing RLS policies on production databases
  • Hardcoded secrets in client-side code
  • Service role keys exposed in frontend

Root causes:

  • RLS not enabled by default
  • Insufficient security guidance in tutorials
  • AI-generated code missing security best practices

Sources:

Supabase Security Improvements (2026)

Current measures:

  • Continuous security testing with red team and purple team
  • Active vulnerability disclosure program on HackerOne
  • Improved RLS guidance and tooling
  • Security audit templates for developers

Source: Supabase Security Retro 2025-2026

Best Practices Summary

1. Always Enable RLS

-- REQUIRED for every table
ALTER TABLE users ENABLE ROW LEVEL SECURITY;

CREATE POLICY "Users can view own data"
  ON users
  FOR SELECT
  USING (auth.uid() = id);

2. Proper Error Handling

const { data, error } = await supabase
  .from('users')
  .select('*');

if (error) {
  if (error.code === '42501') {
    // RLS policy violation
    return { error: 'Access denied' };
  } else if (error.code === '23505') {
    // Unique violation
    return { error: 'Record already exists' };
  }
  throw error; // Unexpected error
}

3. Check isAuthApiError

import { isAuthApiError } from '@supabase/supabase-js';

const { data, error } = await supabase.auth.signUp({ email, password });

if (error) {
  if (isAuthApiError(error)) {
    // API error - check error.code
    if (error.status === 429) {
      return { error: 'Too many attempts. Please try again later.' };
    }
  }
}

4. NEVER Use service_role in Client

// ❌ WRONG - CRITICAL SECURITY RISK
const supabase = createClient(url, serviceRoleKey); // In browser code

// ✅ CORRECT - Use anon key in browser
const supabase = createClient(url, anonKey);

5. Handle returning: 'minimal' for RLS

// If SELECT policy missing after INSERT
const { data, error } = await supabase
  .from('users')
  .insert({ name: 'Alice' })
  .select('*', { returning: 'minimal' });

6. Security Checklist Before Deploy

  • RLS enabled on all tables
  • Policies created for all operations
  • Service role key only in server code
  • Anon key only in client code
  • Auth rate limit handling
  • Error code checking (not string matching)
  • SMTP provider configured
  • Database triggers tested

Verification Date

Last Verified: 2026-02-24 Supabase JS Client Version: 2.x Documentation Version: Current as of February 2026

Need a different package?
Request a profile