{"record":{"id":"b40ee972380b9535","repo":"instructure/canvas-lms","slug":"unable-to-handle-state-change-asked-state","errorCode":null,"errorMessage":"unable to handle state change: #{asked_state}","messagePattern":"unable to handle state change: #(.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/graphql/mutations/create_assignment.rb","lineNumber":48,"sourceCode":"    course_id = GraphQLHelpers.parse_relay_or_legacy_id(input[:course_id], \"Course\")\n\n    @course = Course.find_by(id: course_id)\n    @working_assignment = @course.assignments.build if @course\n\n    raise GraphQL::ExecutionError, \"invalid course: #{course_id}\" unless @working_assignment&.grants_right? current_user, :create\n\n    # initialize published argument\n    @working_assignment.workflow_state = \"unpublished\"\n    input_hash = input.to_h\n    if input_hash.key? :state\n      asked_state = input_hash.delete :state\n      case asked_state\n      when \"unpublished\"\n        input_hash[:published] = false\n      when \"published\"\n        input_hash[:published] = true\n      else\n        raise \"unable to handle state change: #{asked_state}\"\n      end\n    end\n\n    if submittable\n      submittable.assignment = @working_assignment\n    end\n\n    api_proxy = ApiProxy.new(context[:request], @working_assignment, context[:session], current_user, in_app: context[:in_app])\n\n    validate_for_checkpoints(input_hash)\n    if input_hash[:for_checkpoints]\n      @working_assignment.has_sub_assignments = true\n    end\n\n    # modifies input_hash\n    prepare_input_params!(input_hash, api_proxy)\n\n    module_ids = prepare_module_ids!(input_hash)","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/graphql/mutations/create_assignment.rb#L30-L66","documentation":"CreateAssignment mutation's resolve maps an asked state ('unpublished'/'published') into input_hash[:published]; any other value falls through to this raise. It is an internal exhaustive-case guard for the requested workflow state.","triggerScenarios":"Passing a state input value other than published/unpublished (e.g. 'deleted', 'draft', or a typo like 'publish') when creating an assignment via GraphQL.","commonSituations":"Clients copying state values from the update mutation (which accepts 'deleted') into the create mutation; enum typos in GraphQL variables.","solutions":["Only send 'published' or 'unpublished' as the state in createAssignment","Use the same enum type declared in the mutation's input so GraphQL rejects invalid values at validation time","Update the mutation to raise GraphQL::ExecutionError with the offending value for clearer client errors"],"exampleFix":"# before\nraise \"unable to handle state change: #{asked_state}\"\n# after\nraise GraphQL::ExecutionError, \"unable to handle state change: #{asked_state}\"","handlingStrategy":"validation","validationCode":"const allowedStates = ['published', 'unpublished'];\nif (state && !allowedStates.includes(state)) {\n  throw new Error(`state must be one of ${allowedStates.join(', ')}`);\n}","typeGuard":"function isCreateState(s) {\n  return s === 'published' || s === 'unpublished';\n}","tryCatchPattern":"try {\n  await createAssignment({ state });\n} catch (e) {\n  if (/unable to handle state change/.test(e.message)) {\n    showError('Unsupported state for assignment creation; use published or unpublished.');\n  }\n}","preventionTips":["Type the state input as a GraphQL enum limited to create-valid values","Share state constants between client and server code","Do not reuse update-mutation state values (e.g. 'deleted') for create","Add client-side schema validation of mutation variables"],"tags":["graphql","mutation","assignments","state"],"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"}