{"record":{"id":"4055c32a16e158e4","repo":"instructure/canvas-lms","slug":"user-isn-t-allowed-to-edit-global-outcomes","errorCode":null,"errorMessage":"User isn't allowed to edit global outcomes","messagePattern":"User isn't allowed to edit global outcomes","errorType":"exception","errorClass":"Canvas::Migration::Error","httpStatus":null,"severity":"error","filePath":"gems/plugins/academic_benchmark/lib/academic_benchmark/converter.rb","lineNumber":38,"sourceCode":"require \"academic_benchmarks\"\n\nmodule AcademicBenchmark\n  class Converter < Canvas::Migration::Migrator\n    def initialize(settings = {})\n      super(settings, \"academic_benchmark\")\n      @ratings_overrides = settings[:migration_options] || {}\n      @course[:learning_outcomes] = []\n      @partner_id = settings[:partner_id]\n      @partner_key = settings[:partner_key]\n    end\n\n    def export\n      unless content_migration\n        raise Canvas::Migration::Error,\n              \"Missing required content_migration settings\"\n      end\n      unless Account.site_admin.grants_right?(content_migration.user, :manage_global_outcomes)\n        raise Canvas::Migration::Error,\n              \"User isn't allowed to edit global outcomes\"\n      end\n      unless @archive_file\n        unless @partner_id.present? || AcademicBenchmark.ensure_partner_id.nil?\n          raise Canvas::Migration::Error, I18n.t(\"A partner ID is required to use Academic Benchmarks\")\n        end\n        unless @partner_key.present? || AcademicBenchmark.ensure_partner_key.nil?\n          raise Canvas::Migration::Error, I18n.t(\"A partner key is required to use Academic Benchmarks\")\n        end\n      end\n      if outcome_data.present?\n        if outcome_data.instance_of? AcademicBenchmarks::Standards::StandardsForest\n          outcome_data.trees.each do |t|\n            @course[:learning_outcomes] << t.root.build_outcomes(@ratings_overrides)\n          end\n        else\n          @course[:learning_outcomes] << outcome_data.root.build_outcomes(@ratings_overrides)\n        end","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/gems/plugins/academic_benchmark/lib/academic_benchmark/converter.rb#L20-L56","documentation":"Canvas::Migration::Error raised in Converter#export when Account.site_admin.grants_right?(content_migration.user, :manage_global_outcomes) is false. Same permission rule as the top-level academic_benchmark.rb check, enforced inside the converter itself.","triggerScenarios":"Calling export on a converter whose content_migration.user lacks the manage_global_outcomes right on the site admin account.","commonSituations":"Import jobs executed after a user's permissions were downgraded; automation using service accounts without site-admin outcomes permission; multi-shard setups where grants_right? resolves on the wrong account.","solutions":["Run the import with a user holding manage_global_outcomes on Account.site_admin.","Grant the permission via a site-admin role or role override for the service account.","Pre-check with Account.site_admin.grants_right?(cm.user, :manage_global_outcomes) before creating the migration.","Verify cm.user is set and not nil (nil user implies no rights)."],"exampleFix":"// before\ncm = ContentMigration.create!(context: Account.site_admin, user: teacher)\nconverter.export\n// after\nadmin = User.site_admin_user # must hold manage_global_outcomes\ncm = ContentMigration.create!(context: Account.site_admin, user: admin)\nconverter.export","handlingStrategy":"validation","validationCode":"precheck = Account.site_admin.grants_right?(cm.user, :manage_global_outcomes)\nraise PermissionError, 'user lacks manage_global_outcomes' unless precheck","typeGuard":null,"tryCatchPattern":"begin\n  converter.export\nrescue Canvas::Migration::Error => e\n  cm.update_attribute(:last_error, e.message)\n  raise\nend","preventionTips":["Verify the service account's permissions after any role changes.","Run imports only with site-admin users.","Check grants_right? before creating the ContentMigration."],"tags":["permissions","migrations","ruby"],"backgroundTag":"permission-denied","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"}