instructure/canvas-lms · error

unable to handle state change: #

Error message

unable to handle state change: #{asked_state}

What it means

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.

Solutions

  1. Only send 'published' or 'unpublished' as the state in createAssignment
  2. Use the same enum type declared in the mutation's input so GraphQL rejects invalid values at validation time
  3. Update the mutation to raise GraphQL::ExecutionError with the offending value for clearer client errors

Example fix

# before
raise "unable to handle state change: #{asked_state}"
# after
raise GraphQL::ExecutionError, "unable to handle state change: #{asked_state}"
Defensive patterns

Strategy: validation

Validate before calling

const allowedStates = ['published', 'unpublished'];
if (state && !allowedStates.includes(state)) {
  throw new Error(`state must be one of ${allowedStates.join(', ')}`);
}

Type guard

function isCreateState(s) {
  return s === 'published' || s === 'unpublished';
}

Try / catch

try {
  await createAssignment({ state });
} catch (e) {
  if (/unable to handle state change/.test(e.message)) {
    showError('Unsupported state for assignment creation; use published or unpublished.');
  }
}

Prevention

When it happens

Trigger: Passing a state input value other than published/unpublished (e.g. 'deleted', 'draft', or a typo like 'publish') when creating an assignment via GraphQL.

Common situations: Clients copying state values from the update mutation (which accepts 'deleted') into the create mutation; enum typos in GraphQL variables.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15). Data as JSON: /api/errors/b40ee972380b9535. Report an issue: GitHub.

Appendix: source

Thrown at app/graphql/mutations/create_assignment.rb:48

    course_id = GraphQLHelpers.parse_relay_or_legacy_id(input[:course_id], "Course")

    @course = Course.find_by(id: course_id)
    @working_assignment = @course.assignments.build if @course

    raise GraphQL::ExecutionError, "invalid course: #{course_id}" unless @working_assignment&.grants_right? current_user, :create

    # initialize published argument
    @working_assignment.workflow_state = "unpublished"
    input_hash = input.to_h
    if input_hash.key? :state
      asked_state = input_hash.delete :state
      case asked_state
      when "unpublished"
        input_hash[:published] = false
      when "published"
        input_hash[:published] = true
      else
        raise "unable to handle state change: #{asked_state}"
      end
    end

    if submittable
      submittable.assignment = @working_assignment
    end

    api_proxy = ApiProxy.new(context[:request], @working_assignment, context[:session], current_user, in_app: context[:in_app])

    validate_for_checkpoints(input_hash)
    if input_hash[:for_checkpoints]
      @working_assignment.has_sub_assignments = true
    end

    # modifies input_hash
    prepare_input_params!(input_hash, api_proxy)

    module_ids = prepare_module_ids!(input_hash)

View on GitHub (pinned to 1c9f0bb801)