mattermost-community/focalboard · warning · Error

ErrorId.InvalidReadOnlyBoard

ErrorId.InvalidReadOnlyBoard

Error message

InvalidReadOnlyBoard

What it means

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.

Source

Thrown at webapp/src/store/initialLoad.ts:57

            boardsMemberships,
            boardTemplates,
            limits,
            myConfig,
        }
    },
)

export const initialReadOnlyLoad = createAsyncThunk(
    'initialReadOnlyLoad',
    async (boardId: string) => {
        const [board, blocks] = await Promise.all([
            client.getBoard(boardId),
            client.getAllBlocks(boardId),
        ])

        // if no board, read_token invalid
        if (!board) {
            throw new Error(ErrorId.InvalidReadOnlyBoard)
        }

        return {board, blocks}
    },
)

export const loadBoardData = createAsyncThunk(
    'loadBoardData',
    async (boardID: string) => {
        const blocks = await client.getAllBlocks(boardID)
        return {
            blocks,
        }
    },
)

export const loadBoards = createAsyncThunk(
    'loadBoards',

View on GitHub (pinned to a84bbb65e3)

Solutions

  1. Regenerate the share/read-only link and send a fresh one to the user
  2. Verify the board still exists and sharing (read-only access) is still enabled for it
  3. Catch the error and show an 'invalid or expired link' page instead of a blank screen
  4. Check that the read-only token is being sent with the getBoard/getAllBlocks requests

Example fix

// before
const {board, blocks} = await loadBoardOptions(boardId) // throws
// after
try {
  const {board, blocks} = await loadBoardOptions(boardId)
} catch (e) {
  if (e.message === 'InvalidReadOnlyBoard') {
    renderExpiredSharePage()
  }
}
Defensive patterns

Strategy: try-catch

Validate before calling

const board = await client.getBoard(boardId).catch(() => null)
if (!board) {
  showInvalidShareLinkPage()
}

Type guard

function isInvalidReadOnlyBoardError(e: unknown): e is Error {
  return e instanceof Error && e.message === 'InvalidReadOnlyBoard'
}

Try / catch

try {
  const {board, blocks} = await loadBoardOptions(boardId)
} catch (e) {
  if (isInvalidReadOnlyBoardError(e)) {
    render('This shared link is invalid or has expired')
    return
  }
  throw e
}

Prevention

When it happens

Trigger: 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.

Common situations: Shared board links past their expiry, revoked sharing tokens, deleted boards still bookmarked, or mistyped board IDs in shared links.

Related errors


AI-assisted analysis of mattermost-community/focalboard@a84bbb65e3 (2026-08-30). Data as JSON: /api/errors/ba71388763859c8a. Report an issue: GitHub.