{"record":{"id":"496265afd6eef298","repo":"different-ai/openwork","slug":"allowedemaildomains-length-1-this-workspa","errorCode":null,"errorMessage":"${allowedEmailDomains.length === 1 ? `This workspace only allows ${allowedEmailDomains[0]} email addresses.` : `This workspace only allows email addresses from these domains: ${allowedEmailDomains.join(\", \")}.`}","messagePattern":"(.+?) email addresses\\.` : `This workspace only allows email addresses from these domains: (.+?)\\.`\\}","errorType":"exception","errorClass":"OrganizationEmailDomainRestrictionError","httpStatus":null,"severity":"error","filePath":"ee/apps/den-api/src/orgs.ts","lineNumber":957,"sourceCode":"        return {\n          status: \"membership_removed\",\n          invitation,\n        }\n      }\n    }\n\n    return null\n  }\n\n  const organizationRows = await db\n    .select({ allowedEmailDomains: OrganizationTable.allowedEmailDomains })\n    .from(OrganizationTable)\n    .where(eq(OrganizationTable.id, invitation.organizationId))\n    .limit(1)\n\n  const allowedEmailDomains = normalizeStoredAllowedEmailDomains(organizationRows[0]?.allowedEmailDomains)\n  if (!isEmailAllowedForOrganization(allowedEmailDomains, input.email)) {\n    throw new OrganizationEmailDomainRestrictionError(input.email, allowedEmailDomains ?? [])\n  }\n\n  const accepted = await acceptInvitation(invitation, input.userId)\n  if (!accepted) {\n    const currentInvitation = await getInvitationById(input.invitationId)\n    if (currentInvitation && getInvitationStatus(currentInvitation) === \"accepted\") {\n      const removedMember = await findSoftRemovedMemberForUser({\n        organizationId: currentInvitation.organizationId,\n        userId: input.userId,\n      })\n      if (removedMember) {\n        return {\n          status: \"membership_removed\",\n          invitation: currentInvitation,\n        }\n      }\n    }\n    return null","sourceCodeStart":939,"sourceCodeEnd":975,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/ee/apps/den-api/src/orgs.ts#L939-L975","documentation":"This error is raised by OrganizationEmailDomainRestrictionError when a user tries to accept a workspace invitation whose organization restricts membership to specific email domains, and the invitee's email domain is not in the organization's allowedEmailDomains list. The message dynamically lists the single allowed domain or all allowed domains.","triggerScenarios":"Calling the accept-invitation flow (acceptInvitation handler in orgs.ts) where isEmailAllowedForOrganization(normalizeStoredAllowedEmailDomains(org.allowedEmailDomains), input.email) returns false — e.g. accepting with a personal gmail.com address when the org only allows @acme.com.","commonSituations":"User accepts an invite with a different email than the one the admin intended; admin changed allowedEmailDomains after sending the invite; the allowed-domains list is stored in a legacy/normalized format that fails to match; invite forwarded to a colleague with another domain.","solutions":["Use the email address on a domain listed in the organization's allowedEmailDomains to accept the invitation.","Ask the workspace admin to add the invitee's email domain to the organization's allowedEmailDomains settings, then retry.","Re-issue the invitation to the correct email address if the original was wrong.","Verify normalizeStoredAllowedEmailDomains parsing matches how the admin entered the domains (e.g. leading @, casing)."],"exampleFix":"// before\nawait acceptOrgInvitation({ invitationId, userId, email: \"dev@gmail.com\" })\n// after\nawait acceptOrgInvitation({ invitationId, userId, email: \"dev@acme.com\" }) // domain allowed by the org","handlingStrategy":"validation","validationCode":"const allowed = normalizeStoredAllowedEmailDomains(org.allowedEmailDomains)\nif (!isEmailAllowedForOrganization(allowed, userEmail)) {\n  throw new Error(`Email ${userEmail} not allowed; permitted domains: ${(allowed ?? []).join(\", \")}`)\n}","typeGuard":"function emailAllowedFor(email: string, domains: string[] | null): boolean {\n  const suffixes = (domains ?? []).map(d => d.startsWith(\"@\") ? d.toLowerCase() : `@${d.toLowerCase()}`)\n  return suffixes.some(s => email.toLowerCase().endsWith(s))\n}","tryCatchPattern":"try {\n  await acceptInvitation({ invitationId, userId, email })\n} catch (e) {\n  if (e instanceof OrganizationEmailDomainRestrictionError) {\n    return { ok: false, reason: \"domain-restricted\", allowedDomains: e.allowedEmailDomains }\n  }\n  throw e\n}","preventionTips":["Check the invitee email's domain against org settings before sending invitations","Re-validate allowedEmailDomains at accept time, since admins can change them after invites are sent","Re-issue invites to the correct address rather than expecting users to switch emails","Surface allowed domains in the invitation UI so mismatches are caught early"],"tags":["authorization","email","invitation","organization"],"backgroundTag":"email-domain-not-allowed","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}