{"record":{"id":"1fcdeca944689664","repo":"Budibase/budibase","slug":"there-was-a-problem-with-the-invite","errorCode":null,"errorMessage":"There was a problem with the invite","messagePattern":"There was a problem with the invite","errorType":"http","errorClass":null,"httpStatus":400,"severity":"error","filePath":"packages/worker/src/api/controllers/global/users.ts","lineNumber":650,"sourceCode":"  }\n  ctx.body = {\n    message: \"User invites successfully removed.\",\n  }\n}\n\nexport const checkInvite = async (ctx: UserCtx<void, CheckInviteResponse>) => {\n  const { code } = ctx.params\n  const tenantId =\n    typeof ctx.request.query.tenantId === \"string\"\n      ? ctx.request.query.tenantId\n      : undefined\n  let invite\n  try {\n    invite = await cache.invite.getCode(code, tenantId)\n  } catch (e) {\n    console.warn(\"Error getting invite from code\", e)\n    ctx.throw(400, \"There was a problem with the invite\")\n  }\n  ctx.body = {\n    email: invite.email,\n  }\n}\n\nexport const getUserInvites = async (\n  ctx: UserCtx<void, GetUserInvitesResponse>\n) => {\n  try {\n    // Restricted to the currently authenticated tenant\n    ctx.body = await cache.invite.getInviteCodes()\n  } catch (e) {\n    ctx.throw(400, \"There was a problem fetching invites\")\n  }\n}\n\nexport const addWorkspaceIdToInvite = async (\n  ctx: UserCtx<void, UpdateInviteResponse, { code: string; role: string }>","sourceCodeStart":632,"sourceCodeEnd":668,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/worker/src/api/controllers/global/users.ts#L632-L668","documentation":"checkInvite looks up an invitation code in the invite cache (cache.invite.getCode). If the lookup throws — meaning the code does not exist, expired, was deleted, or the cache is down — the handler hides the details and returns 400 'There was a problem with the invite'. This endpoint is normally called by the client to resolve which email an invite code belongs to before the user accepts it.","triggerScenarios":"GET /api/global/users/invite/:code (with optional ?tenantId=) where the code is not found in the invite cache: already accepted, expired and garbage-collected, mistyped, or looked up against the wrong tenantId; also when Redis/invite store is unreachable.","commonSituations":"User opens an invite email days later after the code was removed; admin deletes pending invites while the user still has the email open; multi-tenant invite link missing the tenantId query param; backend cache outage.","solutions":["Ask the admin to resend the invitation so a fresh, valid code is generated.","Ensure the invite link includes the correct tenantId query parameter for multi-tenant deployments.","Check worker logs ('Error getting invite from code') and cache health if codes should still be valid.","If the invite was already accepted, simply sign in instead of following the old link."],"exampleFix":"// before: link without tenant context fails lookup\nresolveInvite(code)\n// after: pass tenantId from the invite URL\nresolveInvite(code, new URLSearchParams(location.search).get('tenantId'))","handlingStrategy":"try-catch","validationCode":"// check the code looks well-formed before hitting the API\nif (!code || code.length < 16) {\n  showError(\"This invite link appears incomplete — ask for a new invitation\")\n}","typeGuard":null,"tryCatchPattern":"try {\n  const { email } = await api.checkInvite(code, tenantId)\n} catch (e) {\n  if (e?.status === 400) showResendInvitePrompt()\n}","preventionTips":["Accept invites promptly before codes expire or are deleted","Preserve the tenantId query param in multi-tenant invite links","Provide a clear 'resend invitation' path in the accept-invite UI"],"tags":["invite","cache","worker","validation"],"backgroundTag":"invite-code-invalid-or-expired","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}