mattermost-community/focalboard · error

cannot fetch board %s for DuplicateBlock: %w

Error message

cannot fetch board %s for DuplicateBlock: %w

What it means

DuplicateBlock fetches the owning board via GetBoard before duplicating a block. This message is returned when GetBoard succeeds (err == nil) but the returned board is nil, meaning the boardID does not resolve to a board. Note the format verb wraps a nil error here, so the trailing ': %!w(<nil>)' may appear in the rendered message; the meaningful part is 'cannot fetch board <id> for DuplicateBlock'.

Source

Thrown at server/app/blocks.go:37

	if blockType != "" && parentID != "" {
		return a.store.GetBlocksWithParentAndType(boardID, parentID, blockType)
	}

	if blockType != "" {
		return a.store.GetBlocksWithType(boardID, blockType)
	}

	return a.store.GetBlocksWithParent(boardID, parentID)
}

func (a *App) DuplicateBlock(boardID string, blockID string, userID string, asTemplate bool) ([]*model.Block, error) {
	board, err := a.GetBoard(boardID)
	if err != nil {
		return nil, err
	}
	if board == nil {
		return nil, fmt.Errorf("cannot fetch board %s for DuplicateBlock: %w", boardID, err)
	}

	blocks, err := a.store.DuplicateBlock(boardID, blockID, userID, asTemplate)
	if err != nil {
		return nil, err
	}

	err = a.CopyAndUpdateCardFiles(boardID, userID, blocks, asTemplate)
	if err != nil {
		return nil, err
	}

	a.blockChangeNotifier.Enqueue(func() error {
		for _, block := range blocks {
			a.wsAdapter.BroadcastBlockChange(board.TeamID, block)
		}
		return nil
	})

View on GitHub (pinned to a84bbb65e3)

Solutions

  1. Verify the boardID exists via App.GetBoard(boardID) before calling DuplicateBlock.
  2. Refresh the client's board list; the board was likely deleted or the ID is stale.
  3. Fix the source bug: this branch wraps a nil err; it should return a sentinel like model.ErrBoardNotFound (or `errors.New` with the boardID) instead of %w on nil.
  4. If the ID came from an API payload, validate it against the team's board list server-side.

Example fix

// before
if board == nil {
    return nil, fmt.Errorf("cannot fetch board %s for DuplicateBlock: %w", boardID, err)
}
// after
if board == nil {
    return nil, fmt.Errorf("cannot fetch board %s for DuplicateBlock: %w", boardID, model.ErrBoardNotFound)
}
Defensive patterns

Strategy: validation

Validate before calling

board, err := app.GetBoard(boardID)
if err != nil || board == nil {
    return fmt.Errorf("board %s not available for duplication", boardID)
}
// safe to call app.DuplicateBlock(boardID, blockID, userID, asTemplate)

Type guard

func boardExists(b *model.Board) bool { return b != nil && b.ID != "" }

Try / catch

blocks, err := app.DuplicateBlock(boardID, blockID, userID, asTemplate)
if err != nil {
    return fmt.Errorf("duplicate block failed (is board %s still valid?): %w", boardID, err)
}

Prevention

When it happens

Trigger: Calling App.DuplicateBlock(boardID, blockID, userID, asTemplate) with a boardID that does not exist in the store, or a board that was deleted concurrently, or a board the store layer filters out (e.g. soft-deleted/permission-filtered), so GetBoard returns (nil, nil).

Common situations: Client caches a stale boardID after the board was deleted by another user; test fixtures use a fabricated board ID without creating the board; race between listing boards and duplicating a block in one.

Related errors


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