{"record":{"id":"4d3ef36e79533453","repo":"instructure/canvas-lms","slug":"subclasses-must-implement-process-batch-when-mode-is-batch","errorCode":null,"errorMessage":"Subclasses must implement #process_batch when mode is :batch","messagePattern":"Subclasses must implement #process_batch when mode is :batch","errorType":"exception","errorClass":"NoMethodError","httpStatus":null,"severity":"error","filePath":"lib/canvas_operations/data_fixup.rb","lineNumber":142,"sourceCode":"      #\n      # Defaults to `true`, which means the data fixup will run on the default shard.\n      attr_writer :run_on_default_shard\n\n      def run_on_default_shard?\n        @run_on_default_shard.nil? || @run_on_default_shard\n      end\n    end\n\n    protected\n\n    # Processes a batch of records. Ideal for making bulk updates to\n    # entire batches of rows that don't need to be individually loaded / processed.\n    #\n    # Subclasses must implement this method to define how to process a batch of records\n    #\n    # Only used when mode is set to :batch.\n    def process_batch(_batch)\n      raise NoMethodError, \"Subclasses must implement #process_batch when mode is :batch\"\n    end\n\n    # Processes an individual record. Ideal for when each record needs to be\n    # loaded and processed separately.\n    #\n    # Subclasses must implement this method to define how to process an individual record.\n    #\n    # Only used when mode is set to :individual_record.\n    def process_record(_record)\n      raise NoMethodError, \"Subclasses must implement #process_record when mode is :individual_record\"\n    end\n\n    # Determines if the current shard is a valid target for the DataFixup operation.\n    #\n    # In its base form this check returns true unless run_on_default_shard is false,\n    # in which case it returns true only if the current shard is not the default shard.\n    #\n    # Be wary if overriding this method. The default shard contains shadow copies of all","sourceCodeStart":124,"sourceCodeEnd":160,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/lib/canvas_operations/data_fixup.rb#L124-L160","documentation":"NoMethodError raised by the DataFixup#process_batch stub. In :batch mode, process_range delegates whole batches to process_batch, which is abstract — subclasses must implement it. This is a template-method enforcement error, not a runtime data problem.","triggerScenarios":"Defining self.mode = :batch on a DataFixup subclass but not overriding process_batch; running a subclass written for the default :individual_record mode after switching it to :batch.","commonSituations":"Converting a slow per-record fixup to batch mode but only renaming process_record; forgetting the override when copy-pasting a new fixup skeleton.","solutions":["Implement def process_batch(batch) ... end in the subclass, processing the scope in bulk (e.g. update_all)","Alternatively set self.mode = :individual_record and implement process_record instead","Check the mode and method names match the intended processing strategy"],"exampleFix":"// before\nself.mode = :batch\n# no process_batch defined\n// after\nself.mode = :batch\n\ndef process_batch(batch)\n  batch.update_all(status: 'fixed')\nend","handlingStrategy":"validation","validationCode":"raise 'process_batch required' if mode == :batch && instance_method(:process_batch).owner == CanvasOperations::DataFixup","typeGuard":null,"tryCatchPattern":"rescue NoMethodError => e\n  raise unless e.message.include?('process_batch')\n  Rails.logger.error('Subclass must implement process_batch for :batch mode')\nend","preventionTips":["Implement process_batch whenever setting mode = :batch","Use inheritance checks/lint rules for abstract methods in fixups","Smoke-test new fixups against a small scope before full runs"],"tags":["ruby","abstract-method","data-fixup","canvas-operations"],"backgroundTag":"abstract-method-not-implemented","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"}