{"record":{"id":"654c6c11bf030e6d","repo":"Budibase/budibase","slug":"audit-logs-not-available-license-required","errorCode":null,"errorMessage":"Audit logs not available - license required.","messagePattern":"Audit logs not available - license required\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/pro/src/sdk/auditLogs/auditLogs.ts","lineNumber":115,"sourceCode":"    const enrichedLog: AuditLogEnriched = {\n      event: log.event,\n      timestamp: log.timestamp,\n      name: log.name,\n      metadata: log.metadata,\n      user: user || deleted(log.userId, ResourceType.USER, log.fallback),\n    }\n    if (log.appId) {\n      enrichedLog.app =\n        workspace || deleted(log.appId, ResourceType.APP, log.fallback)\n    }\n    enriched.push(enrichedLog)\n  }\n  return enriched\n}\n\nexport async function fetch(params: AuditLogSearchParams) {\n  if (!(await isAuditLogsEnabled())) {\n    throw new Error(\"Audit logs not available - license required.\")\n  }\n\n  const filter: SearchFilters = await getSearchFilters(params)\n\n  if (typeof params.bookmark === \"string\") {\n    throw new Error(\"String based bookmark not supported.\")\n  }\n\n  const response = await auditLogs.searchSQL(filter, params.bookmark)\n  return {\n    hasNextPage: response.hasNextPage,\n    bookmark: response.bookmark,\n    data: await enrich(response.rows),\n  }\n}\n\n// like the fetch, but without pagination (uses filtered replication)\nexport function download(params: AuditLogSearchParams): {","sourceCodeStart":97,"sourceCodeEnd":133,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/pro/src/sdk/auditLogs/auditLogs.ts#L97-L133","documentation":"The audit log search function checks isAuditLogsEnabled(), which reflects the instance license. Audit log storage and querying are a paid feature; without the license the fetch throws instead of querying the audit store.","triggerScenarios":"Calling the audit log search API (GET /api/global/auditlogs or equivalent via sdk.auditLogs.fetch) with AuditLogSearchParams on an instance whose license does not include audit logs.","commonSituations":"Free/open-source self-hosted plan attempting to read audit logs; expired or downgraded license; license not yet synced to the running instance after upgrade; calling the API from custom tooling that assumes enterprise edition.","solutions":["Upgrade to a Budibase license that includes audit logs (enterprise/business tier).","If already licensed, force a license refresh/re-sync so isAuditLogsEnabled() picks up the entitlement.","Gate the audit log UI/API calls behind a license check so users see an upsell instead of an error.","Check the account/license key configuration (ACCOUNT_TENANT_ID / license env) in self-hosted deployments."],"exampleFix":"// before\nconst logs = await fetch(params)\n// after\nif (await isAuditLogsEnabled()) {\n  const logs = await fetch(params)\n} else {\n  showUpgradePrompt()\n}","handlingStrategy":"try-catch","validationCode":"const enabled = await isAuditLogsEnabled()\nif (!enabled) throw new UpgradeRequired(\"Audit logs require an upgraded license\")","typeGuard":"function canUseAuditLogs(plan: Plan): boolean {\n  return plan.features.includes(\"auditLogs\")\n}","tryCatchPattern":"try {\n  return await auditLogs.fetch(params)\n} catch (e) {\n  if (e.message.includes(\"license required\")) {\n    showUpgradePrompt(\"auditLogs\")\n  } else throw e\n}","preventionTips":["Check entitlements before rendering audit-log UI","Keep the license key current and force entitlement re-sync after upgrades","Centralize feature-gating checks so paid features degrade gracefully"],"tags":["licensing","audit-logs","feature-gate"],"backgroundTag":"license-required-feature","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}