{"record":{"id":"b16d9d6d0f7048fe","repo":"Budibase/budibase","slug":"invitation-is-not-valid-or-has-expired-please-req","errorCode":null,"errorMessage":"Invitation is not valid or has expired, please request a new one.","messagePattern":"Invitation is not valid or has expired, please request a new one\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/backend-core/src/cache/invite.ts","lineNumber":99,"sourceCode":"function toInviteWithCode(\n  code: string,\n  invite: InviteListEntry\n): InviteWithCode {\n  return {\n    code,\n    email: invite.email,\n    info: invite.info,\n  }\n}\n\nasync function findInviteInList(code: string, tenantId: string) {\n  const client = await redis.getInviteListClient()\n  const list = normaliseInviteList(\n    (await client.get(tenantId)) as InviteListPayload | undefined,\n    tenantId\n  )\n  if (!list) {\n    throw new Error(INVALID_INVITE_MESSAGE)\n  }\n  const inviteCodes = Object.keys(list.invites)\n  if (!inviteCodes.includes(code)) {\n    throw new Error(INVALID_INVITE_MESSAGE)\n  }\n  return { list, invite: list.invites[code] }\n}\n\n/**\n * Given an invite code and invite body, allow the update an existing/valid invite in redis\n * @param code The invite code for an invite in redis\n * @param value The body of the updated user invitation\n */\nexport async function updateCode(code: string, value: Invite) {\n  const info: Invite[\"info\"] = {\n    ...value.info,\n  }\n  const tenantId = info.tenantId || getTenantId()","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/Budibase/budibase/blob/a81a902e9a8fe55b467d106765f6638f12e35c49/packages/backend-core/src/cache/invite.ts#L81-L117","documentation":"In fetchSharePointListDocument's item loop, a non-empty '@odata.nextLink' that fails isAllowedSharePointNextLink (not an https URL under graph.microsoft.com/v1.0) throws this HTTPError, aborting CSV generation. It is the SSRF/allowlist guard applied to list-items pagination.","triggerScenarios":"The items page JSON carries a nextLink that is relative, http://, on another host/port, or not parseable as a URL.","commonSituations":"Corporate proxy rewriting Graph payloads; hand-built mock responses in tests; sovereign cloud hostnames; MITM or corrupted responses.","solutions":["Bypass response-rewriting proxies so Graph's absolute nextLinks arrive intact.","Fix test/mocked payloads to emit absolute https://graph.microsoft.com/v1.0/... nextLinks.","Investigate any TLS-intercepting appliances modifying response bodies.","Confirm the target tenant is on the global Microsoft Graph cloud."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"if (page[\"@odata.nextLink\"] && !/^https:\\/\\/graph\\.microsoft\\.com\\/v1\\.0\\//.test(page[\"@odata.nextLink\"])) {\n  throw new Error(\"Environment is rewriting Microsoft Graph pagination URLs\")\n}","typeGuard":"const isAbsoluteGraphLink = (v: unknown): v is string =>\n  typeof v === \"string\" && (() => { try { return new URL(v).hostname === \"graph.microsoft.com\" } catch { return false } })()","tryCatchPattern":"try {\n  await fetchSharePointListDocument(token, siteId, listId)\n} catch (e) {\n  if (e instanceof Error && e.message === \"Invalid SharePoint pagination URL\") {\n    // audit proxy / mock / TLS-interception layer\n  } else throw e\n}","preventionTips":["Test the environment once with a real Graph paging request before production syncs.","Keep test fixtures using absolute graph.microsoft.com nextLinks.","Exclude Graph traffic from any HTTP body-rewriting appliance."],"tags":["sharepoint","microsoft-graph","pagination","ssrf"],"backgroundTag":"invalid-pagination-url","analyzedSha":"a81a902e9a8fe55b467d106765f6638f12e35c49","analyzedAt":"2026-08-29T01:03:10.972Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}