BuildBaseBuildBase

Error Codes

The error codes and messages the SDK produces, what each one means, and what to do about it.

Most SDK failures are a plain Error whose message says what went wrong. A handful carry a machine-readable code you can branch on. This page lists both: the codes first, then the messages you are most likely to see.

How errors surface

WhereWhat you get
HooksAn error field (usually a string) on the hook result, for example useConnectedAgents(), PricingPage
Async calls you makeA rejected promise with an Error; read err.message, and err.code where this page lists one
onError in the server config(error, { method, path }) runs for every failed API request, before the error is thrown to you
onUnauthorizedOn the *Api client classes (WorkspaceApi and siblings): runs when a request gets a 401
TrackingAn sdk_error event with component, action and code, never the message
import BuildBase from '@buildbase/sdk';

const bb = new BuildBase({
  serverUrl: process.env.BUILDBASE_SERVER_URL!,
  orgId: process.env.BUILDBASE_ORG_ID!,
  onError: (error, { method, path }) => {
    console.error(`[buildbase] ${method} ${path}`, error.message);
  },
});

Aborted requests (an AbortError, from your own AbortController or an unmounted component) are treated as cancellations: the SDK does not report them, and you can ignore them too.

Error codes

Credits

CodeWhen it happensWhat to do
INSUFFICIENT_CREDITSuseConsumeCredits() or bb.credits.consume() gets a 402: the balance is too lowRead err.available and err.requested to explain the gap, then offer openCreditStore() or a plan change
import {
  useConsumeCredits,
  useSaaSAuth,
  useSaaSWorkspaces,
} from '@buildbase/sdk/react';

function Generate() {
  const { currentWorkspace } = useSaaSWorkspaces();
  const { consumeCredits } = useConsumeCredits(currentWorkspace?._id);
  const { openCreditStore } = useSaaSAuth();

  async function run() {
    try {
      await consumeCredits({ amount: 10, description: 'AI generation' });
    } catch (err: any) {
      if (err.code === 'INSUFFICIENT_CREDITS') {
        alert(
          `You have ${err.available} credits, this needs ${err.requested}.`
        );
        openCreditStore();
        return;
      }
      throw err;
    }
  }

  return <button onClick={run}>Generate</button>;
}

Account and security

The calls behind the Profile and Security screens (WorkspaceApi) keep the server's sentence as the message and carry its reason as err.code. Two of them can come from any change the user makes:

CodeWhen it happensWhat to do
reauth_requiredA 403: the change needs a sign-in from the last 10 minutes, and this session is olderisReauthRequired(err), then useSaaSAuth().requireReverification({ factors: err.factors }). See Confirm it's you
read_only_sessionA 403: an app owner is signed in as the user, read-onlyisReadOnlySession(err). Show the message; retrying cannot succeed
impersonation_sign_in_lockedA 403: an app owner is signed in as the user and tried to change how the person signs in (email, phone, username, password, second step, passkeys, linked accounts)Show the message; it applies to every sign-in-as session, read-only or not

Neither signs the user out. The rest are listed with their feature: Two-step sign-in, Phone sign-in, Connected Accounts, Usernames, Guest users and Deleting accounts.

Uploads and storage

An upload through the SDK (useUpload, UploadsApi) rejects with an Error that has err.status and err.code set. uploadFailureOf(err) turns them into a reason you can word yourself - tooLarge, invalid, disabled, userLimit, unavailable or failed - which useUpload also returns as failure. The same codes come back from the console API's asset uploads and from a form's file field, where your own code reads the response body.

CodeStatusWhen it happensWhat to do
UPLOAD_TOO_LARGE413The file is larger than the route, the form field or the organization's end-user limit takes. From an upload intent, the body carries maxBytes, the limit in bytesSay the limit with maxBytes and ask for a smaller file. On the 5 MB multipart route, upload direct instead
INVALID_UPLOAD400The declared size or type is missing or not an image, an end-user upload names an unknown purpose, or what arrived is empty, larger than declared or not a supported imageSend JPEG, PNG, GIF, WebP or SVG with its real size, and pick the file again
UPLOADS_DISABLED403The organization turned end-user uploads offHide the upload control: the public settings answer uploads.enabled: false
USER_UPLOAD_LIMIT403The file would take this user past the organization's per-user total. The body carries heldBytes and limitBytesAsk the user to remove an earlier file, or raise the per-user total in the console
UPLOADS_UNAVAILABLE503, 501503: the organization cannot take the file right now (storage full or a plan limit), said neutrally to an end user or a form visitor. 501: the server has no direct uploads configuredShow a "try later" message. As the organization, read the storage meter; on a self-hosted install, set up direct uploads
UPLOAD_NOT_RECEIVED409complete was called before the PUT to storage finishedComplete once the PUT answers; retry after a moment
UPLOAD_FINALIZING409Another call is completing the same upload right nowRetry after a moment; it answers the same file
INLINE_FILE_TOO_LARGE413A JSON body carries a data: file over 16 KB in one field, named in field, while the storage gate is enforcedUpload the file and send its URL instead
PLAN_LIMIT_REACHED402On the console API, with quotaSlug: "storage-gb": the upload does not fit under the storage ceiling. The body carries storage with usedBytes, reservedBytes, ceilingBytes, wantedBytes and cause (plan, trial or payment)Delete files you no longer need or move to a plan with more storage; for trial, start the plan; for payment, fix the payment method. See Assets

An end user and a form visitor never see PLAN_LIMIT_REACHED: the organization's plan is not theirs to act on, so they get UPLOADS_UNAVAILABLE with 503.

Agent token verification

These are the code values on AppBridgeError, thrown by verifyClientJwt(), verifyAppTokenRequest() and verifyAppRevokeRequest() when a request from BuildBase to your applicationTokenUrl or applicationRevokeUrl fails verification.

CodeWhen it happensWhat to do
invalid_tokenThe token or client secret is missing, the JWT is malformed, or a claim has a bad typeCheck the Authorization: Bearer header reaches your handler intact
invalid_algorithmThe JWT header is not HS256Reject it; BuildBase only signs with HS256
invalid_signatureThe signature does not match the client secretConfirm the client secret in your environment matches the one in the dashboard
invalid_secretsignClientJwt() was called without a signing secretCheck the environment variable holding the client secret is set where the code runs
token_expiredexp has passed, beyond the 60-second clock toleranceCheck your server clock; widen clockToleranceSec only if your clock skew is known
token_not_yet_validnbf is still in the futureSame as above: usually clock skew
invalid_issueriss does not equal the issuer option you passedFix the expected issuer, or drop the option
invalid_audienceaud does not contain the audience option you passedFix the expected audience, or drop the option

handleAppTokenRequest() and handleAppRevokeRequest() catch these for you and return a 401 whose body message is the code, so you only handle AppBridgeError directly when you call the verify functions yourself.

Tracking codes

The code on an sdk_error tracking event is SDK_ERROR when an operation threw an Error, and UNKNOWN_ERROR when it threw something that was not an Error. Use component and action on the same event to find which operation failed.

Common error messages

These have no code; match on the situation rather than the exact string.

MessageWhen it happensWhat to do
Invalid serverUrl: "...". Must be a valid URL.Provider config has a serverUrl that does not parseInclude the protocol, e.g. https://
Invalid orgId: "...". Must be a valid MongoDB ObjectId (24 hexadecimal characters).orgId is not a 24-character hex idCopy the organization id from the dashboard
Invalid version: "...". Only "v1" is currently supported.version is not v1Pass ApiVersion.V1
SDK is not ready (missing serverUrl, version, or orgId)An API call ran before the config was setMount SaaSOSProvider above the component, or pass all three to new BuildBase()
use... must be used within a ...ProviderA hook rendered outside SaaSOSProviderMove the provider higher in the tree
Request timeout after 30000ms: GET ...A request outlived the timeout option (30 seconds by default)Raise timeout, or set maxRetries for transient failures (retries apply to network errors and 5xx)
No workspace selectedA credit purchase ran with no current workspaceWait for useSaaSWorkspaces() to load, or pick a workspace first
Workspace not foundA delete targeted an id that is not in the user's loaded workspace listRefresh the workspace list; the user may have been removed
Only the workspace creator can delete the workspaceA non-owner called deleteHide the action with usePermissions().isOwner
Cannot remove the workspace owner / Cannot change the role of the workspace ownerAn action targeted the ownerFilter the owner out of member actions

Messages from the server

When the API responds with an error, the thrown message is the server's own message when it sends one. When it does not, the SDK falls back to <action> (<status>: <reason>), for example Failed to fetch credit balance (403: Forbidden - You do not have permission):

StatusFallback reasonUsually means
400Bad RequestA required field is missing or malformed
401Unauthorized - Please check your sessionThe session expired or was revoked; sign the user in again
403Forbidden - You do not have permissionThe user's role lacks the permission; see Permissions
404Resource not foundThe id is wrong, or the resource belongs to another workspace
500Internal server errorRetry; if it persists, contact support
502Bad GatewayThe server is unreachable or restarting; retry
503Service unavailableSame as 502

Some methods also attach the HTTP status as err.status. It is not set on every call, so check for it before relying on it.

Next steps