{"record":{"id":"907b1016104fa276","repo":"toeverything/AFFiNE","slug":"doc-action-denied-907b10","errorCode":"doc_action_denied","errorMessage":"You do not have permission to perform ${action} action on doc ${docId}.","messagePattern":"You do not have permission to perform (.+?) action on doc (.+?)\\.","errorType":"exception","errorClass":"DocActionDenied","httpStatus":403,"severity":"error","filePath":"packages/backend/server/src/core/workspaces/controller.ts","lineNumber":65,"sourceCode":"\n  private buildVisitorId(req: Request, workspaceId: string, docId: string) {\n    const tracker = getRequestTrackerId(req);\n    return createHash('sha256')\n      .update(`${workspaceId}:${docId}:${tracker}`)\n      .digest('hex');\n  }\n\n  private async assertCanReadPublicDoc(\n    userId: string,\n    workspaceId: string,\n    docId: string\n  ) {\n    const canReadSharedDoc = await this.ac\n      .user(userId)\n      .doc(workspaceId, docId)\n      .can('Doc.Read');\n    if (!canReadSharedDoc) {\n      throw new DocActionDenied({\n        docId,\n        spaceId: workspaceId,\n        action: 'Doc.Read',\n      });\n    }\n  }\n\n  private async getPublishModeHeader(workspaceId: string, docId: string) {\n    const docMeta = await this.models.doc.getMeta(workspaceId, docId, {\n      select: {\n        mode: true,\n      },\n    });\n    return docMeta?.mode === PublicDocMode.Edgeless\n      ? DocMode.edgeless\n      : DocMode.page;\n  }\n","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/26c515e050211269e911f7d9cfe162a26c83ed98/packages/backend/server/src/core/workspaces/controller.ts#L47-L83","documentation":"Thrown by assertCanReadPublicDoc when the access-control check ac.user(userId).doc(workspaceId, docId).can('Doc.Read') returns false. It guards public/shared document reads (published docs, shared links) so a user without read permission cannot fetch the doc binary or metadata.","triggerScenarios":"A request to read a doc (public doc endpoint, shared link) by a user who lacks Doc.Read on that doc: the doc is not actually published/shared, the share was revoked, or the wrong user identity is being passed.","commonSituations":"A shared link was expired/revoked; a public doc switched back to private; the wrong userId is passed (anonymous vs authenticated); permission propagation lag after a role change.","solutions":["Verify the doc is published/shared and that the requesting user is the intended recipient.","Re-share the doc with the user or re-publish it.","Confirm the correct user identity/session is being sent (CurrentUser).","Check the access-control rules and role assignments for the workspace/doc."],"exampleFix":"// before\nconst can = await ac.user(userId).doc(ws, docId).can('Doc.Read')\nif (!can) proceed() // ignores denial\n// after\nconst can = await ac.user(userId).doc(ws, docId).can('Doc.Read')\nif (!can) throw new Forbidden('ask the owner to share this doc')","handlingStrategy":"validation","validationCode":"const canRead = await ac.user(userId).doc(workspaceId, docId).can('Doc.Read')\nif (!canRead) return showShareRequestUI(workspaceId, docId)","typeGuard":null,"tryCatchPattern":"try { await fetchPublicDoc(ws, docId) } catch (e) {\n  if (e.code === 'doc_action_denied') requestAccess(ws, docId)\n  else throw e\n}","preventionTips":["Check Doc.Read before navigating users into a shared doc.","Surface a 'request access' flow on denial.","Audit share/public doc toggles in the UI."],"tags":["authz","access-control","docs","permissions"],"backgroundTag":null,"analyzedSha":"26c515e050211269e911f7d9cfe162a26c83ed98","analyzedAt":"2026-08-12T13:15:16.447Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}