{"record":{"id":"74f1c166b108409e","repo":"Budibase/budibase","slug":"action-name","errorCode":null,"errorMessage":"${action.name}","messagePattern":"\\$\\{action\\.name\\}","errorType":"exception","errorClass":"UsageLimitWarning","httpStatus":null,"severity":"warning","filePath":"packages/pro/src/sdk/quotas/quotas.ts","lineNumber":388,"sourceCode":"          }\n\n          let triggers: QuotaTriggers = {}\n          if (!action.opts?.dryRun) {\n            triggers = await checkTriggers(\n              action.type,\n              action.name,\n              totalValue,\n              licensedQuota\n            )\n            triggersToApply = { ...triggersToApply, [action.name]: triggers }\n          }\n\n          if (\n            licensedQuota.value !== constants.UNLIMITED &&\n            totalValue > licensedQuota.value &&\n            action.usageChange > 0 // allow for decrementing usage when the quota is already exceeded\n          ) {\n            throw new UsageLimitWarning(`${action.name}`)\n          }\n\n          // never go negative if the quota has previously been exceeded\n          totalValue = Math.max(0, totalValue)\n          if (appValue) {\n            appValue = Math.max(0, appValue)\n          }\n          if (breakdownValue != null) {\n            breakdownValue = Math.max(0, breakdownValue)\n            breakdownValuesToApply = {\n              ...breakdownValuesToApply,\n              [action.name]: breakdownValue,\n            }\n          }\n\n          // Apply functions if it is not a dry run\n          if (!action.opts?.dryRun) {\n            // if a value function is provided","sourceCodeStart":370,"sourceCodeEnd":406,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/pro/src/sdk/quotas/quotas.ts#L370-L406","documentation":"After computing the new total usage for an action, updateUsage compares it against the licensed quota value. If the quota is not UNLIMITED, the total would exceed the limit, and the usageChange is positive (increment), it throws a UsageLimitWarning whose message is simply the quota action name. Decrementing is deliberately allowed so a tenant can climb back down after exceeding a quota. The thrown name is the quota name, not a human sentence — callers are expected to wrap it.","triggerScenarios":"An increment (e.g. creating rows/users/queries/apps) that would push monthly or static usage past the licensed quota value while usage is already at or above the limit.","commonSituations":"Free-plan tenant hitting row or user caps; automation bulk-creating records that overshoot a quota mid-run; license downgrade shrinking a quota below current usage so every subsequent increment throws; apps/users import exceeding static quotas.","solutions":["Free up usage by deleting resources (or wait for monthly reset) before incrementing again","Upgrade the plan/license to raise the specific quota named in the message","Catch UsageLimitWarning and map the quota name to a user-friendly limit message","Review bulk operations to pre-check quota headroom before large imports"],"exampleFix":"// before: name-only warning propagates raw\ntry { await createRows(rows) } catch (e) { throw e }\n// after: translate the quota name\ntry {\n  await createRows(rows)\n} catch (err) {\n  if (err instanceof UsageLimitWarning) {\n    throw new Error(`You have reached your ${err.message} quota limit — upgrade to continue`)\n  }\n  throw err\n}","handlingStrategy":"try-catch","validationCode":"const current = await quotas.getUsage(action.name)\nconst limit = await quotas.getLicensedLimit(action.name)\nif (limit !== constants.UNLIMITED && current + usageChange > limit) {\n  // block operation up-front and show upgrade prompt\n}","typeGuard":null,"tryCatchPattern":"try {\n  await createResource()\n} catch (err) {\n  if (err instanceof UsageLimitWarning) {\n    throw new Error(`Quota limit reached: ${err.message} — upgrade your plan`)\n  }\n  throw err\n}","preventionTips":["Check quota headroom before bulk imports or automations","Map thrown quota names to friendly UI messages","Reconcile usage after plan downgrades to restore headroom"],"tags":["quotas","usage-limit","billing"],"backgroundTag":"quota-limit-exceeded","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}