{"record":{"id":"91aa632da5f30a97","repo":"instructure/canvas-lms","slug":"unable-to-delete-commentbankitem","errorCode":null,"errorMessage":"Unable to delete CommentBankItem","messagePattern":"Unable to delete CommentBankItem","errorType":"exception","errorClass":"GraphQL::ExecutionError","httpStatus":null,"severity":"error","filePath":"app/graphql/mutations/delete_comment_bank_item.rb","lineNumber":37,"sourceCode":"#\n\nclass Mutations::DeleteCommentBankItem < Mutations::BaseMutation\n  graphql_name \"DeleteCommentBankItem\"\n\n  argument :id, ID, required: true, prepare: GraphQLHelpers.relay_or_legacy_id_prepare_func(\"CommentBankItem\")\n  field :comment_bank_item_id, ID, null: false\n\n  def resolve(input:)\n    record = CommentBankItem.active.find_by(id: input[:id])\n    raise GraphQL::ExecutionError, I18n.t(\"Unable to find CommentBankItem\") if record.nil?\n\n    verify_authorized_action!(record, :delete)\n    context[:deleted_models][:comment_bank_item] = record\n\n    if record.destroy\n      { comment_bank_item_id: record.id }\n    else\n      raise GraphQL::ExecutionError, I18n.t(\"Unable to delete CommentBankItem\")\n    end\n  end\n\n  def self.comment_bank_item_id_log_entry(_entry, context)\n    context[:deleted_models][:comment_bank_item]\n  end\nend\n","sourceCodeStart":19,"sourceCodeEnd":45,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/graphql/mutations/delete_comment_bank_item.rb#L19-L45","documentation":"After authorization succeeds, DeleteCommentBankItem calls record.destroy; if destroy returns false (the model refused to delete, typically due to a before_destroy callback aborting or validation-adjacent failure), the mutation raises \"Unable to delete CommentBankItem\". Unlike the not-found case, the record exists but could not be removed.","triggerScenarios":"Calling deleteCommentBankItem on an item whose destroy callback aborts; database constraint or hook preventing deletion; concurrent modification making destroy fail.","commonSituations":"Item referenced elsewhere and a before_destroy guard aborts; DB replication/constraint issue; plugin or shard-level callback raising silently and returning false.","solutions":["Check CommentBankItem model callbacks (before_destroy) for abort conditions and logs for the actual reason.","Retry the deletion; transient DB issues can make destroy return false.","Inspect Rails logs around the mutation for suppressed errors during destroy.","As an admin, verify no shard/replica lag is causing the record state to conflict."],"exampleFix":"// before\nif record.destroy\n  { comment_bank_item_id: record.id }\nelse\n  raise GraphQL::ExecutionError, \"Unable to delete CommentBankItem\"\nend\n// after\nif record.destroy\n  { comment_bank_item_id: record.id }\nelse\n  raise GraphQL::ExecutionError, record.errors.full_messages.to_sentence.presence || \"Unable to delete CommentBankItem\"\nend","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await deleteCommentBankItem({ id })\n} catch (e) {\n  if (e.message.includes('Unable to delete CommentBankItem')) {\n    retryWithBackoff(() => deleteCommentBankItem({ id }))\n      .catch(() => reportServerError('comment bank delete failed'));\n  } else throw e;\n}","preventionTips":["Retry transient delete failures with backoff.","Check server logs for before_destroy aborts when it reproduces.","Avoid concurrent edits/deletes of the same comment bank item."],"tags":["graphql","delete-failed","persistence"],"backgroundTag":"database-write-failed","analyzedSha":"1c9f0bb8013ed69c4f2efe11fd483025469b7e6c","analyzedAt":"2026-09-15T20:33:18.891Z","contentChangedAt":"2026-09-15T20:33:18.891Z","schemaVersion":2},"datasetVersion":"2026-09-23T02:17:17.105Z"}