{"record":{"id":"3a19c642ac83e022","repo":"toeverything/AFFiNE","slug":"workspace-license-already-exists","errorCode":"workspace_license_already_exists","errorMessage":"Workspace already has a license applied.","messagePattern":"Workspace already has a license applied\\.","errorType":"exception","errorClass":"WorkspaceLicenseAlreadyExists","httpStatus":403,"severity":"error","filePath":"packages/backend/server/src/plugins/license/service.ts","lineNumber":195,"sourceCode":"      workspaceId: this.licenseWorkspaceId(resolved),\n      plan: SubscriptionPlan.SelfHostedTeam,\n      recurring: this.licenseRecurring(resolved),\n      quantity: this.licenseQuantity(resolved),\n      issuedAt: new Date(resolved.issuedAt ?? ''),\n      expiresAt,\n      endAt: expiresAt,\n      entity: resolved.entity ?? '',\n      issuer: resolved.issuer ?? '',\n      valid: true,\n    };\n  }\n\n  @Transactional()\n  async activateTeamLicense(workspaceId: string, licenseKey: string) {\n    const installedLicense = await this.getLicense(workspaceId);\n\n    if (installedLicense) {\n      throw new WorkspaceLicenseAlreadyExists();\n    }\n    const occupiedLicense = await this.db.installedLicense.findUnique({\n      where: { key: licenseKey },\n    });\n    if (occupiedLicense) {\n      throw new WorkspaceLicenseAlreadyExists();\n    }\n\n    const data = this.remoteLicense(\n      await licenseClient.activate({ licenseKey })\n    );\n\n    const validatedAt = new Date();\n    const expiresAt = new Date(data.expiresAt);\n\n    const installed = await this.db.installedLicense.create({\n      data: {\n        workspaceId,","sourceCodeStart":177,"sourceCodeEnd":213,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/b4c8548c09da21b2898443559a5b846f0ccf5dd8/packages/backend/server/src/plugins/license/service.ts#L177-L213","documentation":"activateTeamLicense() is the transactional flow that binds a license key (obtained from the AFFiNE Pro cloud) to a workspace. It first checks the local installedLicense table for the workspace and throws WorkspaceLicenseAlreadyExists if one is already bound. The guard keeps the one-license-per-workspace invariant before contacting the remote license service.","triggerScenarios":"Calling activateTeamLicense(workspaceId, licenseKey) when getLicense(workspaceId) already returns a row — e.g. double activation, retrying an activation that actually succeeded, or activating a second license without removing the first.","commonSituations":"Frontend double-submit or retry after a timeout where the first request committed; admin switching plans and forgetting to call removeTeamLicense first; race between two admins activating different keys simultaneously.","solutions":["Check for an existing license first: call getLicense(workspaceId) (or the admin settings UI) and remove it with removeTeamLicense(workspaceId) before activating.","If this was a duplicate submit, ignore the error — the workspace already has the intended license; verify via getLicense that the installed key matches.","In UIs, disable the activate button while a request is in flight to prevent double activation."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const existing = await licenseService.getLicense(workspaceId);\nif (existing) {\n  // already activated — remove first if switching: await licenseService.removeTeamLicense(workspaceId);\n} else {\n  await licenseService.activateTeamLicense(workspaceId, licenseKey);\n}","typeGuard":"function isWorkspaceLicenseAlreadyExists(e: unknown): e is WorkspaceLicenseAlreadyExists {\n  return e instanceof WorkspaceLicenseAlreadyExists;\n}","tryCatchPattern":"try {\n  await licenseService.activateTeamLicense(workspaceId, licenseKey);\n} catch (e) {\n  if (e instanceof WorkspaceLicenseAlreadyExists) {\n    const current = await licenseService.getLicense(workspaceId);\n    if (current?.key !== licenseKey) throw new Error('Remove the existing license before activating a new one');\n    // else: idempotent success — the intended key is already active\n  } else throw e;\n}","preventionTips":["Fetch the current license state before showing the activation form.","Debounce/disable the activate button while a request is in flight.","Treat 'already exists' as idempotent success when the installed key matches."],"tags":["license","self-hosted","idempotency","duplicate"],"backgroundTag":"license-already-activated","analyzedSha":"b4c8548c09da21b2898443559a5b846f0ccf5dd8","analyzedAt":"2026-08-18T21:16:52.546Z","contentChangedAt":"2026-08-18T21:16:52.546Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}