{"record":{"id":"e584f45a1bae334d","repo":"instructure/canvas-lms","slug":"invalid-value-must-be-integer-value","errorCode":null,"errorMessage":"invalid value, must be integer: #{value}","messagePattern":"invalid value, must be integer: #(.+?)","errorType":"validation","errorClass":null,"httpStatus":500,"severity":"error","filePath":"app/controllers/outcomes_academic_benchmark_import_api_controller.rb","lineNumber":99,"sourceCode":"    end\n  end\n\n  ##\n  # valid guids can only contain hex digits (letters all upper case),\n  # and must be separated between a '-' [8-4-4-4-12]\n  #\n  #   example:  A833C528-901A-11DF-A622-0C319DFF4B22\n  ##\n  def valid_guid(guid)\n    unless /[A-F0-9]{8}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{12}/i.match?(guid)\n      raise \"GUID is invalid: #{guid}\"\n    end\n  end\n\n  def parse_int(value)\n    Integer value\n  rescue\n    raise \"invalid value, must be integer: #{value}\"\n  end\n\n  def valid_options(hash)\n    options = {}\n    if hash.key? :calculation_method\n      options[:calculation_method] = parse_calculation_method hash[:calculation_method]\n      options[:calculation_int] = parse_calculation_int options[:calculation_method], hash[:calculation_int]\n    end\n    options[:mastery_points] = parse_int hash[:mastery_points] if hash.key? :mastery_points\n    options[:points_possible] = parse_int hash[:points_possible] if hash.key? :points_possible\n    unless hash[:ratings].nil?\n      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","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/controllers/outcomes_academic_benchmark_import_api_controller.rb#L81-L117","documentation":"OutcomesAcademicBenchmarkImportApiController#parse_int converts supplied values with Kernel#Integer and raises if the value is not an integer. It backs valid_options, parse_calculation_int, and parse_rating, so any numeric outcome attribute (mastery_points, points_possible, calculation_int, rating points) must be integer-typed.","triggerScenarios":"Passing mastery_points/points_possible/calculation_int/rating points as a non-integer string like '0.5', 'high', '' or nil-adjacent values through the import API's valid_options hash.","commonSituations":"Clients sending decimal points (e.g. 1.5 points possible) for outcomes; JSON bodies with string numbers containing units or commas; forms submitting empty strings instead of omitting the field.","solutions":["Send integers (or integer strings) for mastery_points, points_possible, calculation_int, and rating points.","Omit the key entirely instead of sending an empty string when the value is not set.","Round decimals client-side before submission.","Validate with a numeric integer regex (^-?\\d+$) before calling the API."],"exampleFix":"// before\n{ mastery_points: \"3.5\" }\n// after\n{ mastery_points: 3 }","handlingStrategy":"validation","validationCode":"def ensure_int(value)\n  Integer(value)\nrescue TypeError, ArgumentError\n  raise ArgumentError, \"must be integer: #{value}\"\nend","typeGuard":"def int_like?(v) = /^-?\\d+$/.match?(v.to_s.strip)","tryCatchPattern":"begin\n  import_outcomes(options)\nrescue RuntimeError => e\n  raise InvalidOutcomeOption, e.message if e.message.start_with?('invalid value, must be integer:')\n  raise\nend","preventionTips":["Send integers, not decimals or decorated strings.","Omit optional keys rather than sending empty strings.","Round fractional points before submission.","Validate all numeric fields with ^-?\\d+$ first."],"tags":["api","validation","integer-parsing"],"backgroundTag":"invalid-argument-format","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"}