{"record":{"id":"9d5772402489ecb6","repo":"Budibase/budibase","slug":"license-does-not-allow-use-of-recaptcha","errorCode":null,"errorMessage":"License does not allow use of recaptcha","messagePattern":"License does not allow use of recaptcha","errorType":"http","errorClass":"ForbiddenError","httpStatus":403,"severity":"error","filePath":"packages/worker/src/api/controllers/global/configs.ts","lineNumber":270,"sourceCode":"  if (existing) {\n    for (const c of config.configs) {\n      const existingConfig = existing.configs.find(e => e.uuid === c.uuid)\n      if (!existingConfig) {\n        continue\n      }\n      if (c.clientSecret === PASSWORD_REPLACEMENT) {\n        c.clientSecret = existingConfig.clientSecret\n      }\n    }\n  }\n}\n\nexport async function processRecaptchaConfig(\n  config: RecaptchaInnerConfig,\n  existingConfig?: RecaptchaInnerConfig\n) {\n  if (!(await pro.features.isRecaptchaEnabled())) {\n    throw new ForbiddenError(\"License does not allow use of recaptcha\")\n  }\n  if (config.secretKey === PASSWORD_REPLACEMENT && !existingConfig) {\n    throw new BadRequestError(\"No secret key provided\")\n  }\n  if (config.secretKey === PASSWORD_REPLACEMENT && existingConfig) {\n    config.secretKey = existingConfig.secretKey\n  }\n}\n\nfunction prepareTranslationsConfig(\n  ctx: UserCtx,\n  config?: TranslationsConfigInner\n): TranslationsConfigInner {\n  const defaultLocale = config?.defaultLocale || \"en\"\n  const locales: TranslationsConfigInner[\"locales\"] = {}\n  const now = new Date().toISOString()\n  const updatedBy = ctx.user?._id\n","sourceCodeStart":252,"sourceCodeEnd":288,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/worker/src/api/controllers/global/configs.ts#L252-L288","documentation":"processRecaptchaConfig gates reCAPTCHA behind a license feature. If pro.features.isRecaptchaEnabled() returns false, saving a reCAPTCHA config throws ForbiddenError regardless of the payload contents.","triggerScenarios":"Any attempt to save a reCAPTCHA config (type recaptcha) on a tenant whose license does not include the reCAPTCHA feature.","commonSituations":"Free-tier or expired license attempting to configure reCAPTCHA on the auth/self-host settings; license key not applied in the environment so features resolve to disabled.","solutions":["Upgrade to a license that includes reCAPTCHA support","Verify the license key is correctly installed and not expired, then re-fetch entitlements","Remove the reCAPTCHA config from the save payload and use an alternative captcha/bot protection","After upgrading, retry the save with the same payload"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const recaptchaAllowed = await pro.features.isRecaptchaEnabled()\nif (!recaptchaAllowed) {\n  // hide recaptcha config section; don't build the save payload\n  return\n}","typeGuard":null,"tryCatchPattern":"try {\n  await configApi.save(recaptchaConfig)\n} catch (err) {\n  if (err.status === 403 && err.message.includes(\"recaptcha\")) {\n    // show upgrade prompt instead of retrying\n  } else { throw err }\n}","preventionTips":["Gate reCAPTCHA UI behind the entitlement check","Confirm license key is installed and valid before configuring premium features","Handle 403 ForbiddenError as an upgrade signal, not a bug","Avoid copying recaptcha config payloads between tenants with different licenses"],"tags":["recaptcha","licensing","entitlements","forbidden"],"backgroundTag":"license-feature-gated","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}