{"record":{"id":"d1a4aba2326a2cf5","repo":"toeverything/AFFiNE","slug":"not-found-d1a4ab","errorCode":"not_found","errorMessage":"Invitation not found","messagePattern":"Invitation not found","errorType":"http","errorClass":"NotFound","httpStatus":404,"severity":"error","filePath":"packages/backend/server/src/core/workspaces/service.ts","lineNumber":56,"sourceCode":"    private readonly runtime: BackendRuntimeProvider\n  ) {}\n\n  async getInviteInfo(inviteId: string): Promise<InviteInfo> {\n    // invite link\n    const invite = await this.cache.get<InviteInfo>(\n      `workspace:inviteLinkId:${inviteId}`\n    );\n    if (typeof invite?.workspaceId === 'string') {\n      return {\n        ...invite,\n        isLink: true,\n      };\n    }\n\n    const workspaceUser = await this.models.workspaceUser.getById(inviteId);\n\n    if (!workspaceUser) {\n      throw new NotFound('Invitation not found');\n    }\n\n    return {\n      isLink: false,\n      workspaceId: workspaceUser.workspaceId,\n      inviteeUserId: workspaceUser.userId,\n      inviterUserId: workspaceUser.inviterId,\n    };\n  }\n\n  async getWorkspaceInfo(workspaceId: string) {\n    const workspaceContent = await this.doc.getWorkspaceContent(workspaceId);\n\n    let avatar = DEFAULT_WORKSPACE_AVATAR;\n    if (workspaceContent?.avatarKey) {\n      const avatarBlob = await this.blobStorage.get(\n        workspaceId,\n        workspaceContent.avatarKey","sourceCodeStart":38,"sourceCodeEnd":74,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/26c515e050211269e911f7d9cfe162a26c83ed98/packages/backend/server/src/core/workspaces/service.ts#L38-L74","documentation":"Thrown by `WorkspaceService.getInviteInfo` when neither a cached link invite (`workspace:inviteLinkId:${inviteId}`) nor a `workspace_user` row by that id exists. The invitation id is unresolvable. Coded under the generic `not_found` (resource_not_found) category with the literal message 'Invitation not found'.","triggerScenarios":"Calling `getInviteInfo(inviteId)` (directly or via the resolver) with an id that is not a link invite in the cache and not a `workspace_user` id. Typoed id, expired/evicted link, revoked email invite, or an id from a different environment.","commonSituations":"Old link invite whose Redis entry TTL'd; admin deleted the pending invitation row; id was truncated when copied; link from staging used against production; the invitation was already accepted and the row mutated.","solutions":["Request a fresh invitation from the workspace owner/admin.","Verify the invite id is complete and unmodified (URL-decode it first if from a URL).","If you operate the cache, confirm Redis is reachable and the invite-link TTL is sane.","Differentiate link ids from email-invite ids in the client to avoid cross-use."],"exampleFix":"// before\nconst info = await workspaceService.getInviteInfo(inviteId);\n\n// after\ntry {\n  const info = await workspaceService.getInviteInfo(inviteId);\n} catch (e) {\n  if (e.code === 'not_found') {\n    throw new ClientVisibleError('This invitation no longer exists. Please ask for a new one.');\n  }\n  throw e;\n}","handlingStrategy":"try-catch","validationCode":"// Best-effort pre-check via the resolver-level getInviteInfo\nconst info = await sdk.getInviteInfo({ inviteId }).catch(() => null);\nif (!info) {\n  throw new ClientVisibleError('This invitation no longer exists.');\n}","typeGuard":"function isResolvableInvite(invite) {\n  return Boolean(invite && (invite.isLink || invite.workspaceId));\n}","tryCatchPattern":"try {\n  const info = await workspaceService.getInviteInfo(inviteId);\n} catch (e) {\n  if (e.code === 'not_found') {\n    throw new ClientVisibleError('Invitation invalid or expired. Request a new one.');\n  }\n  throw e;\n}","preventionTips":["Distinguish link ids from email-invite ids client-side to avoid cross-use.","URL-decode invite ids before lookup.","Request fresh invitations when this error fires; do not retry the same id."],"tags":["service","workspace","invitation","cache","redis","not-found","nestjs"],"backgroundTag":null,"analyzedSha":"26c515e050211269e911f7d9cfe162a26c83ed98","analyzedAt":"2026-08-12T13:15:16.447Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}