{"record":{"id":"ee3ad044c88435c8","repo":"instructure/canvas-lms","slug":"invalid-targetcontexttype","errorCode":null,"errorMessage":"Invalid targetContextType","messagePattern":"Invalid targetContextType","errorType":"exception","errorClass":"GraphQL::ExecutionError","httpStatus":null,"severity":"error","filePath":"app/graphql/mutations/import_outcomes.rb","lineNumber":339,"sourceCode":"          \"You must provide targetGroupId or targetContextId and targetContextType\"\n        )\n      elsif input[:target_context_type].blank? && input[:target_context_id].present?\n        raise GraphQL::ExecutionError, I18n.t(\n          \"targetContextType required if targetContextId provided\"\n        )\n      elsif input[:target_context_type].present? && input[:target_context_id].blank?\n        raise GraphQL::ExecutionError, I18n.t(\n          \"targetContextId required if targetContextType provided\"\n        )\n      end\n\n      target_context =\n        begin\n          context_class(input[:target_context_type]).find_by(\n            id: input[:target_context_id]\n          )\n        rescue NameError\n          raise GraphQL::ExecutionError, I18n.t(\"Invalid targetContextType\")\n        end\n\n      if target_context.nil?\n        raise GraphQL::ExecutionError, I18n.t(\"no such target context\")\n      end\n\n      [target_context, target_context.root_outcome_group]\n    end\n  end\n\n  def context_class(context_type)\n    raise NameError unless VALID_CONTEXTS.include? context_type\n\n    context_type.constantize\n  end\n\n  def process_job(source_context:, target_group:, group: nil, outcome_id: nil)\n    target_context = target_group.context","sourceCodeStart":321,"sourceCodeEnd":357,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/graphql/mutations/import_outcomes.rb#L321-L357","documentation":"ImportOutcomes#get_target resolves the target context via context_class(input[:target_context_type]) (e.g. Course, Account). If the type string does not correspond to a known class, the lookup raises NameError, which is rescued and re-raised as this ExecutionError.","triggerScenarios":"Passing targetContextType values other than Course/Account, misspellings ('course', lowercase), or empty-but-present strings to the importOutcomes mutation.","commonSituations":"Clients using human-readable labels ('account') instead of the GraphQL class names; case-sensitivity mistakes after switching from enums to strings; typos in saved API scripts.","solutions":["Use the exact class names 'Course' or 'Account' for targetContextType","Validate the type value client-side against the allowed set before calling the mutation","Check for case/whitespace issues in the value being sent"],"exampleFix":"// before\ninput: { targetContextType: \"account\", targetContextId: 7 }\n// after\ninput: { targetContextType: \"Account\", targetContextId: 7 }","handlingStrategy":"validation","validationCode":"const ALLOWED = ['Course', 'Account'];\nif (input.targetContextType && !ALLOWED.includes(input.targetContextType)) {\n  throw new Error(`targetContextType must be one of ${ALLOWED.join(', ')}`);\n}","typeGuard":"const isValidContextType = (t) => t === 'Course' || t === 'Account';","tryCatchPattern":"try {\n  await client.mutate({ mutation: IMPORT_OUTCOMES, variables: { input } });\n} catch (e) {\n  if (e.message.includes('Invalid targetContextType')) {\n    // normalize the type string to 'Course' or 'Account' and retry once\n  }\n}","preventionTips":["Use exact class names 'Course' and 'Account', never lowercase labels","Derive the type from a fixed constant, not user input","Trim and case-normalize strings before sending"],"tags":["graphql","validation","invalid-enum","outcomes"],"backgroundTag":"invalid-enum-value","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"}