{"record":{"id":"d7eb9bd70c946b3e","repo":"instructure/canvas-lms","slug":"operation-is-being-run-on-the-wrong-shard-expected-switchman","errorCode":null,"errorMessage":"Operation is being run on the wrong shard. Expected #{switchman_shard.id}, got #{Shard.current.id}","messagePattern":"Operation is being run on the wrong shard\\. Expected #(.+?), got #(.+?)","errorType":"exception","errorClass":"Errors::WrongShard","httpStatus":null,"severity":"critical","filePath":"lib/canvas_operations/base_operation.rb","lineNumber":131,"sourceCode":"    #       callbacks (`before_run`, `after_run`, `around_run`).\n    # @note When using `run_later`, the progress tracker is managed automatically by `Progress#process_job` unless\n    #       progress_tracking is disabled.\n    #\n    # @note If the operation raises `Errors::InvalidOperationTarget`, the operation gracefully fails, runs any\n    #       failure callbacks, and marks the progress as failed.\n    #\n    # @param new_progress [Progress] The progress tracker object to be used for this operation. Note that\n    #   `Progress#process_job` manages this automatically when using `run_later`.\n    # @raise [Errors::WrongShard] If the operation is run on an incorrect shard.\n    # @return [void]\n    def run(new_progress = nil)\n      @progress = new_progress if new_progress\n\n      run_callbacks :run do\n        unless Shard.current == switchman_shard\n          # We intentionally do not call fail_with_error! here because we are on the wrong shard and subclasses may be\n          # making assumptions about what shard the failure callbacks run on.\n          raise Errors::WrongShard, \"Operation is being run on the wrong shard. Expected #{switchman_shard.id}, got #{Shard.current.id}\"\n        end\n\n        execute\n\n        complete_progress\n      end\n    rescue Errors::InvalidOperationTarget => e\n      log_message(\"Operation failed due to invalid operation target: #{e.message}\", level: :error)\n      log_message(\"Note that the above error is being rescued; if this is a migration, other migrations can still continue.\", level: :info)\n\n      results[:error] = e.message\n\n      fail_with_error!\n    end\n\n    # Marks the operation as failed and updates the progress with the current results.\n    #\n    # When `#run_later` is used, the Progress lifecycle is managed automatically, and this method","sourceCodeStart":113,"sourceCodeEnd":149,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/lib/canvas_operations/base_operation.rb#L113-L149","documentation":"CanvasOperations::Errors::WrongShard raised inside BaseOperation#run when Shard.current does not match the operation's switchman_shard. Because Canvas is sharded via Switchman, running the operation on the wrong shard could corrupt data or run callbacks against the wrong database, so it fails hard instead of using fail_with_error! (failure callbacks may assume the correct shard).","triggerScenarios":"Calling operation.run from a context whose current shard differs from the shard the operation was built/assigned to — e.g. enqueuing on the default shard and executing on Shard.default instead of the target account's shard.","commonSituations":"Background job workers that don't call Shard.wrap/activate_shard before running; creating the operation on a root account but running it under a different shard in a console or scheduled job.","solutions":["Wrap execution in the correct shard: operation.switchman_shard.activate { operation.run } or Shard.wrap(operation.switchman_shard.id) { ... }","Rebuild the operation on the shard you intend to run it on","In delayed jobs, persist and re-activate the shard (e.g. via Switchman's job extension) before run"],"exampleFix":"// before\nDataFixupOperation.for(account).run\n// after\naccount.shard.activate { DataFixupOperation.for(account).run }","handlingStrategy":"try-catch","validationCode":"raise 'wrong shard' unless Shard.current == operation.switchman_shard","typeGuard":null,"tryCatchPattern":"rescue CanvasOperations::Errors::WrongShard\n  operation.switchman_shard.activate { operation.run }\nend","preventionTips":["Always activate the target shard before run in background jobs and consoles","Persist shard info with the job payload (Switchman job extensions)","Add an assertion in job handlers that Shard.current matches the operation"],"tags":["ruby","sharding","switchman","multi-tenancy"],"backgroundTag":"internal-invariant-violation","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"}