{"record":{"id":"ba71388763859c8a","repo":"mattermost-community/focalboard","slug":"errorid-invalidreadonlyboard","errorCode":"ErrorId.InvalidReadOnlyBoard","errorMessage":"InvalidReadOnlyBoard","messagePattern":"InvalidReadOnlyBoard","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"webapp/src/store/initialLoad.ts","lineNumber":57,"sourceCode":"            boardsMemberships,\n            boardTemplates,\n            limits,\n            myConfig,\n        }\n    },\n)\n\nexport const initialReadOnlyLoad = createAsyncThunk(\n    'initialReadOnlyLoad',\n    async (boardId: string) => {\n        const [board, blocks] = await Promise.all([\n            client.getBoard(boardId),\n            client.getAllBlocks(boardId),\n        ])\n\n        // if no board, read_token invalid\n        if (!board) {\n            throw new Error(ErrorId.InvalidReadOnlyBoard)\n        }\n\n        return {board, blocks}\n    },\n)\n\nexport const loadBoardData = createAsyncThunk(\n    'loadBoardData',\n    async (boardID: string) => {\n        const blocks = await client.getAllBlocks(boardID)\n        return {\n            blocks,\n        }\n    },\n)\n\nexport const loadBoards = createAsyncThunk(\n    'loadBoards',","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/mattermost-community/focalboard/blob/a84bbb65e32edf972856b329417096ac413518e9/webapp/src/store/initialLoad.ts#L39-L75","documentation":"loadBoardOptions throws new Error(ErrorId.InvalidReadOnlyBoard) when a board fetch returns nothing while in read-only (shared board) mode. In read-only mode a missing board means the read-only token was invalid or the board no longer exists.","triggerScenarios":"Opening a shared/read-only board link where client.getBoard(boardId) returns null because the read token is invalid/expired or the board was deleted.","commonSituations":"Shared board links past their expiry, revoked sharing tokens, deleted boards still bookmarked, or mistyped board IDs in shared links.","solutions":["Regenerate the share/read-only link and send a fresh one to the user","Verify the board still exists and sharing (read-only access) is still enabled for it","Catch the error and show an 'invalid or expired link' page instead of a blank screen","Check that the read-only token is being sent with the getBoard/getAllBlocks requests"],"exampleFix":"// before\nconst {board, blocks} = await loadBoardOptions(boardId) // throws\n// after\ntry {\n  const {board, blocks} = await loadBoardOptions(boardId)\n} catch (e) {\n  if (e.message === 'InvalidReadOnlyBoard') {\n    renderExpiredSharePage()\n  }\n}","handlingStrategy":"try-catch","validationCode":"const board = await client.getBoard(boardId).catch(() => null)\nif (!board) {\n  showInvalidShareLinkPage()\n}","typeGuard":"function isInvalidReadOnlyBoardError(e: unknown): e is Error {\n  return e instanceof Error && e.message === 'InvalidReadOnlyBoard'\n}","tryCatchPattern":"try {\n  const {board, blocks} = await loadBoardOptions(boardId)\n} catch (e) {\n  if (isInvalidReadOnlyBoardError(e)) {\n    render('This shared link is invalid or has expired')\n    return\n  }\n  throw e\n}","preventionTips":["Set sensible expiry/rotation for share tokens and communicate it to users","Check board existence before sharing links","Render a friendly expired-link page instead of a crash","Log token failures to detect revoked or mistyped links"],"tags":["typescript","frontend","read-only","sharing"],"backgroundTag":"invalid-share-token","analyzedSha":"a84bbb65e32edf972856b329417096ac413518e9","analyzedAt":"2026-08-30T09:22:20.720Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}