{"record":{"id":"97fc3ffd7cb0802c","repo":"mattermost-community/focalboard","slug":"cannot-patch-card-s-w","errorCode":null,"errorMessage":"cannot patch card %s: %w","messagePattern":"cannot patch card (.+?): %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/app/cards.go","lineNumber":73,"sourceCode":"\t\tb := blk\n\t\tif card, err := model.Block2Card(b); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"Block2Card fail: %w\", err)\n\t\t} else {\n\t\t\tcards = append(cards, card)\n\t\t}\n\t}\n\treturn cards, nil\n}\n\nfunc (a *App) PatchCard(cardPatch *model.CardPatch, cardID string, userID string, disableNotify bool) (*model.Card, error) {\n\tblockPatch, err := model.CardPatch2BlockPatch(cardPatch)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tnewBlock, err := a.PatchBlockAndNotify(cardID, blockPatch, userID, disableNotify)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"cannot patch card %s: %w\", cardID, err)\n\t}\n\n\tnewCard, err := model.Block2Card(newBlock)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn newCard, nil\n}\n\nfunc (a *App) GetCardByID(cardID string) (*model.Card, error) {\n\tcardBlock, err := a.GetBlockByID(cardID)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tcard, err := model.Block2Card(cardBlock)\n\tif err != nil {","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/mattermost-community/focalboard/blob/a84bbb65e32edf972856b329417096ac413518e9/server/app/cards.go#L55-L91","documentation":"PatchCard wraps any failure from PatchBlockAndNotify (the underlying block patch operation) with the card ID for context, using %w so the root cause remains inspectable via errors.Is/As. If this error surfaces, the card block could not be updated in the store — common causes are the block not existing, permission failures, or a store/database error. The original error from the patch layer is preserved as the wrapped cause.","triggerScenarios":"Calling App.PatchCard with a cardID that does not exist in the store; the underlying block store returns an error during the patch (DB down, connection refused); the userID lacks permission to modify the block; or CardPatch2BlockPatch produced an invalid patch that PatchBlockAndNotify rejects.","commonSituations":"Client holds a stale card ID after the card was deleted by another user; concurrent modification conflicts; database migrations or connectivity issues in self-hosted deployments; REST API consumers patching cards via /api/v1/cards/{cardID} with an invalid or deleted ID.","solutions":["Verify the cardID exists (GetCardByID) before patching and handle ErrNotFound distinctly","Check the wrapped cause with errors.Is/As to distinguish not-found from permission vs store errors","Verify database connectivity and that the store service is healthy","Confirm the acting userID has edit permission on the parent board"],"exampleFix":"// before\nnewCard, err := app.PatchCard(patch, cardID, userID, false)\nif err != nil {\n\treturn err\n}\n// after\nnewCard, err := app.PatchCard(patch, cardID, userID, false)\nif err != nil {\n\tif errors.Is(err, model.ErrNotFound) {\n\t\treturn fmt.Errorf(\"card %s not found\", cardID)\n\t}\n\treturn fmt.Errorf(\"patch card %s: %w\", cardID, err)\n}","handlingStrategy":"validation","validationCode":"if _, err := app.GetCardByID(cardID); err != nil {\n\treturn fmt.Errorf(\"card %s not patchable: %w\", cardID, err)\n}\n// also validate patch\ncardPatch := &model.CardPatch{...}\nif _, err := model.CardPatch2BlockPatch(cardPatch); err != nil {\n\treturn err\n}","typeGuard":"func isCardPatchNotFound(err error) bool {\n\treturn errors.Is(err, model.ErrNotFound)\n}","tryCatchPattern":"newCard, err := app.PatchCard(patch, cardID, userID, false)\nif err != nil {\n\tswitch {\n\tcase errors.Is(err, model.ErrNotFound):\n\t\t// stale ID: refresh card list\n\tdefault:\n\t\treturn fmt.Errorf(\"patch card %s: %w\", cardID, err)\n\t}\n}","preventionTips":["Fetch the card (GetCardByID) before patching to confirm it exists","Always inspect the wrapped cause with errors.Is/As instead of matching the message string","Keep client-side card state fresh to avoid patching deleted cards","Check the acting user's board permissions before issuing patches"],"tags":["go","wrapping","cards","store"],"backgroundTag":"wrapped-causal-error","analyzedSha":"a84bbb65e32edf972856b329417096ac413518e9","analyzedAt":"2026-08-30T09:22:20.720Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}