{"record":{"id":"ce1f4eb0545e12db","repo":"instructure/canvas-lms","slug":"invalid-calculation-int-value","errorCode":null,"errorMessage":"invalid calculation_int: #{value}","messagePattern":"invalid calculation_int: #(.+?)","errorType":"validation","errorClass":null,"httpStatus":500,"severity":"error","filePath":"app/controllers/outcomes_academic_benchmark_import_api_controller.rb","lineNumber":129,"sourceCode":"      raise \"ratings expected to be an array\" unless hash[:ratings].is_a?(Array)\n\n      options[:ratings] = hash[:ratings].map { |r| parse_rating(r) }\n    end\n    options\n  end\n\n  def parse_calculation_method(value)\n    unless LearningOutcome.valid_calculation_method?(value)\n      raise \"invalid calculation_method: #{value}\"\n    end\n\n    value\n  end\n\n  def parse_calculation_int(calc_method, value)\n    int = value.nil? ? nil : parse_int(value)\n    unless LearningOutcome.valid_calculation_int?(int, calc_method)\n      raise \"invalid calculation_int: #{value}\"\n    end\n\n    int\n  end\n\n  def parse_rating(rating)\n    if rating.nil? || !rating.is_a?(ActionController::Parameters)\n      raise \"invalid ratings value: #{rating}\"\n    end\n    if rating[:description].nil? || !rating[:description].is_a?(String)\n      raise \"invalid description value: #{rating[:description]}\"\n    end\n\n    { description: rating[:description], points: parse_int(rating[:points]) }\n  end\nend\n","sourceCodeStart":111,"sourceCodeEnd":146,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/controllers/outcomes_academic_benchmark_import_api_controller.rb#L111-L146","documentation":"OutcomesAcademicBenchmarkImportApiController#parse_calculation_int validates the calculation_int against LearningOutcome.valid_calculation_int? for the chosen calculation method and raises if invalid. calculation_int is only meaningful for methods like decaying_average and n_mastery, with method-specific ranges.","triggerScenarios":"Sending calculation_int without (or with an incompatible) calculation_method, or a value outside the allowed range — e.g. decaying_average requires 1-99, n_mastery requires >= 2 — or a non-integer caught earlier by parse_int.","commonSituations":"Sending calculation_int: 100 with decaying_average (max 99); omitting calculation_method so validation fails; sending calculation_int for 'latest'/'highest' where it is not allowed.","solutions":["For decaying_average send an integer 1-99; for n_mastery send an integer >= 2, paired with the matching calculation_method.","Omit calculation_int for methods that do not use it (latest, highest, average).","Check LearningOutcome.valid_calculation_int?(int, method) client-side.","Parse as integer first (no decimals or strings with units)."],"exampleFix":"// before\n{ calculation_method: \"decaying_average\", calculation_int: 100 }\n// after\n{ calculation_method: \"decaying_average\", calculation_int: 75 }","handlingStrategy":"validation","validationCode":"raise ArgumentError unless (1..99).cover?(calculation_int) if method == 'decaying_average'\nraise ArgumentError unless calculation_int.is_a?(Integer) && calculation_int >= 2 if method == 'n_mastery'","typeGuard":"def valid_calc_int?(int, method) = LearningOutcome.valid_calculation_int?(int, method)","tryCatchPattern":"begin\n  import_outcomes(options)\nrescue RuntimeError => e\n  raise InvalidCalculationInt, e.message if e.message.start_with?('invalid calculation_int:')\n  raise\nend","preventionTips":["Pair calculation_int with a compatible calculation_method.","Respect method ranges: decaying_average 1-99, n_mastery >= 2.","Omit calculation_int for latest/highest/average.","Validate against LearningOutcome.valid_calculation_int? first."],"tags":["api","outcome","validation"],"backgroundTag":"value-out-of-range","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"}