{"record":{"id":"dbfade89206f5f6a","repo":"instructure/canvas-lms","slug":"individual-ratings-data-input-with-acount-level-mastery","errorCode":null,"errorMessage":"individual ratings data input with acount_level_mastery_scale FF enabled","messagePattern":"individual ratings data input with acount_level_mastery_scale FF enabled","errorType":"exception","errorClass":"GraphQL::ExecutionError","httpStatus":null,"severity":"error","filePath":"app/graphql/mutations/base_learning_outcome_mutation.rb","lineNumber":48,"sourceCode":"    argument :title, String, required: true\n    argument :vendor_guid, String, required: false\n\n    field :learning_outcome, Types::LearningOutcomeType, null: true\n\n    protected\n\n    def attrs(input, context)\n      outcome_input = input.to_h.slice(:title, :display_name, :description, :vendor_guid)\n\n      outcome_input.merge ratings_attrs(input, context)\n    end\n\n    def ratings_attrs(input, context)\n      ratings_input = input.to_h.slice(:calculation_method, :calculation_int, :mastery_points, :ratings)\n\n      return {} unless ratings_input.count.positive? && context\n\n      raise GraphQL::ExecutionError, I18n.t(\"individual ratings data input with acount_level_mastery_scale FF enabled\") if context.root_account.feature_enabled?(:account_level_mastery_scales)\n\n      updated_ratings_attrs = {}\n      rubric_criterion = {}\n      rubric_criterion[:mastery_points] = ratings_input[:mastery_points] if ratings_input[:mastery_points]\n      rubric_criterion[:ratings] = ratings_input[:ratings] if ratings_input[:ratings]\n      updated_ratings_attrs[:calculation_method] = ratings_input[:calculation_method] if ratings_input[:calculation_method]\n      updated_ratings_attrs[:calculation_int] = ratings_input[:calculation_int] if ratings_input[:calculation_int]\n      updated_ratings_attrs[:rubric_criterion] = rubric_criterion if rubric_criterion.present?\n\n      updated_ratings_attrs\n    end\n  end\nend\n","sourceCodeStart":30,"sourceCodeEnd":62,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/graphql/mutations/base_learning_outcome_mutation.rb#L30-L62","documentation":"base_learning_outcome_mutation#ratings_attrs rejects individual-outcome ratings input (calculation_method, calculation_int, mastery_points, ratings) when the root account has the account_level_mastery_scales feature flag enabled. With that flag on, mastery scales must be managed at the account level, so per-outcome ratings data in the mutation input is an unsupported operation and raises this ExecutionError.","triggerScenarios":"Calling createLearningOutcome/updateLearningOutcome with input containing calculation_method, calculation_int, mastery_points, or ratings while context.root_account.feature_enabled?(:account_level_mastery_scales) is true and a context is present.","commonSituations":"Clients built before the account-level mastery scales feature send legacy per-outcome ratings payloads against an account where the flag was turned on; scripts or LTI tools updating mastery points directly on outcomes.","solutions":["Remove calculation_method/calculation_int/mastery_points/ratings from the mutation input","Manage mastery scales via the account-level mastery scale endpoints instead","Disable account_level_mastery_scales on the root account if per-outcome ratings must still be used (not recommended)","Update client integrations to detect the flag and branch their payloads"],"exampleFix":"// before\nupdateLearningOutcome(input: { id: $id, masteryPoints: 3, ratings: $ratings })\n// after\n// with account_level_mastery_scales enabled:\nupdateLearningOutcome(input: { id: $id, title: $title }) // manage scales at account level","handlingStrategy":"validation","validationCode":"const account = await canvas.get(`/api/v1/accounts/${rootAccountId}/features`)\nconst scalesEnabled = account.includes('account_level_mastery_scales')\nif (scalesEnabled && (input.masteryPoints || input.ratings || input.calculationMethod)) {\n  throw new Error('per-outcome ratings not allowed with account_level_mastery_scales enabled')\n}","typeGuard":"function hasRatingsInput(i) { return i != null && ['calculationMethod','calculationInt','masteryPoints','ratings'].some(k => i[k] !== undefined) }","tryCatchPattern":"try {\n  await client.mutate({ mutation: UPDATE_LEARNING_OUTCOME, variables: { input } })\n} catch (e) {\n  if (e.message.includes('individual ratings data input')) {\n    // strip ratings fields and retry with account-level scales\n  }\n}","preventionTips":["Query the root account's enabled feature flags before building outcome mutations","Branch client payloads on account_level_mastery_scales","Migrate mastery-scale management to account-level endpoints","Document in integrations that per-outcome ratings are legacy"],"tags":["graphql","mastery-scales","feature-flag","outcomes"],"backgroundTag":"unsupported-operation","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"}