{"record":{"id":"3e417fbf3dcb6ea6","repo":"Stirling-Tools/Stirling-PDF","slug":"no-team-to-invite-to","errorCode":null,"errorMessage":"No team to invite to","messagePattern":"No team to invite to","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"frontend/editor/src/saas/portal/usersBackend.ts","lineNumber":228,"sourceCode":"  fetchAuthConfig(): Promise<AdminAuthConfig> {\n    // SaaS is Supabase-authed: no direct password create, no self-hosted\n    // OAuth/SAML provider list. Static, no network call (the login probe is an\n    // admin/self-hosted endpoint).\n    return Promise.resolve({\n      canDirectCreate: false,\n      hasOauth: false,\n      hasSaml: false,\n    });\n  },\n\n  async inviteMember(\n    email: string,\n    _role: Extract<RoleId, \"admin\" | \"member\">,\n    teamId?: number,\n  ): Promise<InviteResult> {\n    // SaaS invitations are always plain members; role is ignored.\n    const tid = teamId ?? (await resolveTeam())?.teamId;\n    if (tid == null) throw new Error(\"No team to invite to\");\n    await apiClient.local.json(`/api/v1/team/invite`, {\n      method: \"POST\",\n      body: { teamId: tid, email },\n    });\n    return { successCount: 1, failureCount: 0 };\n  },\n\n  async renameTeam(teamId: number, newName: string): Promise<void> {\n    await apiClient.local.json(`/api/v1/team/${teamId}/rename`, {\n      method: \"POST\",\n      body: { newName },\n    });\n  },\n\n  async removeMember(member: Member): Promise<void> {\n    if (member.teamId == null) {\n      throw new Error(\"Member has no team to be removed from\");\n    }","sourceCodeStart":210,"sourceCodeEnd":246,"githubUrl":"https://github.com/Stirling-Tools/Stirling-PDF/blob/9ef20dcab80b85041912f045e17a6aea1d08f969/frontend/editor/src/saas/portal/usersBackend.ts#L210-L246","documentation":"Thrown by the SaaS usersBackend inviteMember() when neither the explicit teamId parameter nor resolveTeam() yields a team ID. resolveTeam() queries the current user's team context; if the user has no team (new account, personal team not yet created, or team fetch failed), tid is null and inviting is impossible.","triggerScenarios":"inviteMember() is called before the user's team context has loaded, or the user genuinely has no team. The teamId parameter is optional and resolveTeam() returns null/undefined.","commonSituations":"New SaaS user whose personal team hasn't been provisioned yet; team context fetch race condition where the admin portal loads before teams resolve; user in an inconsistent account state after a team deletion.","solutions":["Ensure the user has a team provisioned before enabling the invite UI (disable the invite button until currentTeam resolves)","Pass an explicit teamId from the caller rather than relying on resolveTeam() fallback","If the user has no team, show a 'create team' prompt instead of allowing invite","Retry resolveTeam() or re-fetch teams before throwing"],"exampleFix":"// before\nconst tid = teamId ?? (await resolveTeam())?.teamId;\nif (tid == null) throw new Error(\"No team to invite to\");\n\n// after\nconst tid = teamId ?? (await resolveTeam())?.teamId;\nif (tid == null) {\n  throw new Error(\n    \"No team found. Please create or join a team before inviting members.\",\n  );\n}","handlingStrategy":"validation","validationCode":"// Validate team exists before showing invite UI\nconst team = await resolveTeam();\nif (!team?.teamId) {\n  setCanInvite(false);\n  showCreateTeamPrompt();\n  return;\n}\nsetCanInvite(true);","typeGuard":null,"tryCatchPattern":"try {\n  await inviteMember(email, role);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('No team')) {\n    showCreateTeamPrompt();\n  } else {\n    throw e;\n  }\n}","preventionTips":["Disable the invite button until resolveTeam() returns a valid teamId","Ensure team provisioning completes during user onboarding before exposing team management UI","Pass an explicit teamId from the caller instead of relying on the resolveTeam() fallback"],"tags":["team","invitation","validation","saas","portal"],"backgroundTag":null,"analyzedSha":"9ef20dcab80b85041912f045e17a6aea1d08f969","analyzedAt":"2026-08-13T22:11:39.827Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}