{"record":{"id":"7b3856c1f995fafb","repo":"mattermost-community/focalboard","slug":"cannot-get-block-s-w","errorCode":null,"errorMessage":"cannot get block %s: %w","messagePattern":"cannot get block (.+?): %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/app/boards.go","lineNumber":80,"sourceCode":"\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tboardMetadata := model.BoardMetadata{\n\t\tBoardID:                 boardID,\n\t\tDescendantFirstUpdateAt: earliestTime,\n\t\tDescendantLastUpdateAt:  latestTime,\n\t\tCreatedBy:               board.CreatedBy,\n\t\tLastModifiedBy:          lastModifiedBy,\n\t}\n\treturn board, &boardMetadata, nil\n}\n\n// getBoardForBlock returns the board that owns the specified block.\nfunc (a *App) getBoardForBlock(blockID string) (*model.Board, error) {\n\tblock, err := a.GetBlockByID(blockID)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"cannot get block %s: %w\", blockID, err)\n\t}\n\n\tboard, err := a.GetBoard(block.BoardID)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"cannot get board %s: %w\", block.BoardID, err)\n\t}\n\n\treturn board, nil\n}\n\nfunc (a *App) getBoardHistory(boardID string, latest bool) (*model.Board, error) {\n\topts := model.QueryBoardHistoryOptions{\n\t\tLimit:      1,\n\t\tDescending: latest,\n\t}\n\tboards, err := a.store.GetBoardHistory(boardID, opts)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not get history for board: %w\", err)","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/mattermost-community/focalboard/blob/a84bbb65e32edf972856b329417096ac413518e9/server/app/boards.go#L62-L98","documentation":"getBoardForBlock resolves the board owning a block, used by notifySubscriptionChanged. This error wraps the failure of App.GetBlockByID(blockID): the block could not be loaded, so the parent board cannot be determined. It propagates the underlying store error (e.g. not-found, permission denied, database failure) under a contextual message.","triggerScenarios":"notifySubscriptionChanged(blockID) fires after a block change but GetBlockByID fails: the block was deleted before the notification step, the blockID is invalid, or the store/database errors out.","commonSituations":"Rapid delete-then-update sequences where the subscription notification runs after the block row is gone; database connectivity problems; webhook/plugin code passing a wrong blockID.","solutions":["Inspect the wrapped cause with errors.Unwrap / errors.Is to distinguish not-found from a real store failure.","If the block was just deleted, treat this as expected: skip the notification instead of surfacing the error.","Verify the blockID against the board's blocks before operating on it.","Check database health and store logs if the cause is not 'not found'."],"exampleFix":"// before\nblock, err := a.GetBlockByID(blockID)\nif err != nil {\n    return nil, fmt.Errorf(\"cannot get block %s: %w\", blockID, err)\n}\n// after\nblock, err := a.GetBlockByID(blockID)\nif err != nil {\n    if errors.Is(err, store.ErrNotFound) {\n        return nil, nil // block already deleted; nothing to notify\n    }\n    return nil, fmt.Errorf(\"cannot get block %s: %w\", blockID, err)\n}","handlingStrategy":"try-catch","validationCode":"if blockID == \"\" {\n    return errors.New(\"skip subscription notification: empty blockID\")\n}\nblock, err := app.GetBlockByID(blockID)\nif err != nil {\n    // block gone or store failure; skip notify\n    return nil\n}","typeGuard":"func isNotFound(err error) bool {\n    return errors.Is(err, store.ErrNotFound) || errors.Is(err, sql.ErrNoRows)\n}","tryCatchPattern":"board, err := app.getBoardForBlock(blockID)\nif err != nil {\n    if isNotFound(errors.Unwrap(err)) {\n        return nil // block deleted; nothing to notify\n    }\n    return fmt.Errorf(\"subscription notify failed: %w\", err)\n}","preventionTips":["Delete blocks and cancel pending notifications atomically where possible.","Check errors.Is on the unwrapped cause to separate not-found from infra errors.","Log the raw blockID and cause before swallowing, to debug orphaned notifications."],"tags":["block","lookup","wrapped-error","subscriptions"],"backgroundTag":"block-not-found","analyzedSha":"a84bbb65e32edf972856b329417096ac413518e9","analyzedAt":"2026-08-30T09:22:20.720Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}