{"record":{"id":"a589a8df61482934","repo":"Budibase/budibase","slug":"app-context-required-for-quota-update","errorCode":null,"errorMessage":"App context required for quota update","messagePattern":"App context required for quota update","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/pro/src/sdk/quotas/quotas.ts","lineNumber":342,"sourceCode":"    let breakdownValuesToApply: any = {}\n    let triggersToApply: any = {}\n    for (let action of actions) {\n      await tracer.trace(\"quotas.updateUsage.action\", async span => {\n        span.addTags({\n          actionName: action.name,\n          actionType: action.type,\n          actionUsageChange: action.usageChange,\n        })\n        try {\n          appId = context.getWorkspaceId()\n        } catch (err) {\n          // ignore error for now\n        }\n\n        // need an app ID for these\n        const isAppQuota = APP_QUOTA_NAMES.includes(action.name)\n        if (isAppQuota && !appId) {\n          throw new Error(\"App context required for quota update\")\n        }\n\n        try {\n          licensedQuota = await getLicensedQuota(\n            QuotaType.USAGE,\n            action.name,\n            action.type\n          )\n          licenseQuotas.push(licensedQuota)\n\n          let {\n            total: totalValue,\n            app: appValue,\n            breakdown: breakdownValue,\n          } = await db.quotas.getCurrentUsageValues(\n            action.type,\n            action.name,\n            action.opts?.id","sourceCodeStart":324,"sourceCodeEnd":360,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/pro/src/sdk/quotas/quotas.ts#L324-L360","documentation":"Some quota names in APP_QUOTA_NAMES are tracked per-app and therefore require an appId to attribute usage. In updateUsage, when such an action is processed without an appId in the context, the function throws this error rather than recording usage against an unknown app. It is an internal invariant guard on the quota update payload.","triggerScenarios":"Calling updateUsage/tryIncrement/decrement/usageLimitIsExceeded with an action whose name is an app-scoped quota (e.g. per-app queries, rows) while the options omit appId; an app-context-less code path (e.g. tenant-level cron or global API) touching an app quota.","commonSituations":"Calling quota SDK functions from workspace-agnostic code (worker jobs, global API handlers) that never resolve appId; refactoring that dropped appId from the options object; automations running outside an app execution context incrementing app-scoped quotas.","solutions":["Pass the current appId in the options when calling updateUsage/tryIncrement for app-scoped quotas","Confirm the action name really is app-scoped — if it is tenant-level, it should not be in APP_QUOTA_NAMES","Resolve appId from the request/automation context before recording usage","If hit from a global context, split the logic so app quotas are only updated within an app context"],"exampleFix":"// before: incrementing an app-scoped quota without app context\nawait quotas.tryIncrement({ name: MonthlyQuotaName.QUERIES, usageChange: 1 })\n// after\nawait quotas.tryIncrement({\n  name: MonthlyQuotaName.QUERIES,\n  usageChange: 1,\n  appId: ctx.appId,\n})","handlingStrategy":"validation","validationCode":"if (APP_QUOTA_NAMES.includes(action.name) && !appId) {\n  throw new Error(`${action.name} requires appId`)\n}","typeGuard":"function hasAppContext(opts: { name: string; appId?: string }): boolean {\n  return !APP_QUOTA_NAMES.includes(opts.name) || Boolean(opts.appId)\n}","tryCatchPattern":"try {\n  await updateUsage(action)\n} catch (err: any) {\n  if (err.message === \"App context required for quota update\") {\n    // resolve appId from request/automation context and retry\n  } else { throw err }\n}","preventionTips":["Always thread appId into quota updates from app-scoped code paths","Audit tenant-level jobs for app-scoped quota increments","Keep APP_QUOTA_NAMES and call sites in sync when adding quotas"],"tags":["quotas","appid","internal"],"backgroundTag":"missing-context-parameter","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}