{"record":{"id":"2510ea30d5141ad2","repo":"mattermost-community/focalboard","slug":"history-not-found-for-board-w","errorCode":null,"errorMessage":"history not found for board: %w","messagePattern":"history not found for board: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/app/boards.go","lineNumber":113,"sourceCode":"\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)\n\t}\n\tif len(boards) == 0 {\n\t\treturn nil, nil\n\t}\n\n\treturn boards[0], nil\n}\n\nfunc (a *App) getBoardDescendantModifiedInfo(boardID string, latest bool) (int64, string, error) {\n\tboard, err := a.getBoardHistory(boardID, latest)\n\tif err != nil {\n\t\treturn 0, \"\", err\n\t}\n\tif board == nil {\n\t\treturn 0, \"\", fmt.Errorf(\"history not found for board: %w\", err)\n\t}\n\n\tvar timestamp int64\n\tmodifiedBy := board.ModifiedBy\n\tif latest {\n\t\ttimestamp = board.UpdateAt\n\t} else {\n\t\ttimestamp = board.CreateAt\n\t}\n\n\t// use block_history to fetch blocks in case they were deleted and no longer exist in blocks table.\n\topts := model.QueryBlockHistoryOptions{\n\t\tLimit:      1,\n\t\tDescending: latest,\n\t}\n\tblocks, err := a.store.GetBlockHistoryDescendants(boardID, opts)\n\tif err != nil {\n\t\treturn 0, \"\", fmt.Errorf(\"could not get blocks history descendants for board: %w\", err)","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/mattermost-community/focalboard/blob/a84bbb65e32edf972856b329417096ac413518e9/server/app/boards.go#L95-L131","documentation":"getBoardDescendantModifiedInfo treats a nil result from getBoardHistory as 'no history rows exist for this board' and returns 'history not found for board'. As written it wraps err, which is nil at that point, so the formatted message contains '%!w(<nil>)'; the semantic meaning is that the board has no history entries at all.","triggerScenarios":"GetBoardMetadata (via getBoardDescendantModifiedInfo) on a board that has never had a history record inserted — e.g. boards created before history tracking existed, boards created directly in the DB, or newly created boards whose first history row has not been written.","commonSituations":"Legacy data predating the board-history feature; imports/migrations that skipped history; a bug where board creation does not seed a history entry.","solutions":["Treat missing history as empty metadata on the caller side instead of an error, if acceptable for your use case.","Create/update the board once through the app API so a history row is generated.","Backfill history rows for legacy boards via a migration.","Fix the source bug: return a dedicated sentinel (e.g. ErrHistoryNotFound) rather than wrapping nil err."],"exampleFix":"// before\nif board == nil {\n    return 0, \"\", fmt.Errorf(\"history not found for board: %w\", err)\n}\n// after\nif board == nil {\n    return 0, \"\", ErrBoardHistoryNotFound\n}","handlingStrategy":"fallback","validationCode":"// detect the missing-history condition up front\n_, err := app.GetBoard(boardID)\nif err != nil {\n    return err\n}\n// no way to pre-check history rows via public API; fall back on error","typeGuard":null,"tryCatchPattern":"meta, err := app.GetBoardMetadata(boardID, latest)\nif err != nil {\n    if strings.Contains(err.Error(), \"history not found for board\") {\n        meta = &model.BoardMetadata{} // fall back to empty metadata\n    } else {\n        return err\n    }\n}","preventionTips":["Seed a history row whenever a board is created, including imports and manual inserts.","Backfill history for boards created before history tracking existed.","Treat 'no history' as empty metadata rather than a hard failure in consumers."],"tags":["history","not-found","nil-wrap","board-metadata"],"backgroundTag":"history-not-found","analyzedSha":"a84bbb65e32edf972856b329417096ac413518e9","analyzedAt":"2026-08-30T09:22:20.720Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}