instructure/canvas-lms · warning

DataFixup::MoveFeatureFlagsToSettings => unable to handle…

Error message

DataFixup::MoveFeatureFlagsToSettings => unable to handle override state for context #{context.asset_string} of feature #{override.id} with state #{override.state}

What it means

A warning logged by the DataFixup::MoveFeatureFlagsToSettings data fixup when it encounters a FeatureFlagOverride whose state is not one of 'on', 'off', or 'locked' that it can map to a value. The fixup cannot translate the override into the new settings storage, so it logs and skips that override rather than migrating it.

Solutions

  1. Inspect the offending rows: SELECT * FROM feature_flag_overrides WHERE state NOT IN ('on','off','locked'); and decide the correct value per row.
  2. Normalize the unexpected states to 'on'/'off'/'locked' (based on the feature's current behavior) and re-run the fixup.
  3. If the state is genuinely meaningless (deleted feature/context), delete the orphan override row, then re-run the fixup.
  4. Extend the fixup's case/when mapping if the new settings schema has a representation for that state.

Example fix

// before
UPDATE feature_flag_overrides SET state = 'on' WHERE state = 'allowed';
// after
UPDATE feature_flag_overrides SET state = 'on' WHERE state NOT IN ('on','off','locked');
-- then re-run DataFixup::MoveFeatureFlagsToSettings.run
Defensive patterns

Strategy: validation

Validate before calling

bad = FeatureFlagOverride.where.not(state: %w[on off locked])
bad.find_each do |o|
  Rails.logger.warn("un-migratable override: feature=#{o.feature} state=#{o.state}")
end

Type guard

def valid_override_state?(state)
  %w[on off locked].include?(state)
end

Try / catch

FeatureFlagOverride.find_each do |override|
  unless %w[on off locked].include?(override.state)
    next # or normalize before running the fixup
  end
end

Prevention

When it happens

Trigger: Running the move_feature_flags_to_settings fixup over feature_flag_overrides rows containing a state value outside {'locked','on','off'} — typically stale, corrupted, or legacy rows written before the state enum was normalized.

Common situations: Long-lived production databases with overrides from old Canvas versions whose state strings were renamed or removed; manual DB edits or partial migrations leaving unexpected state values; running the fixup against a restored/partial dataset.

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/6a505989090c8495. Report an issue: GitHub.

Appendix: source

Thrown at lib/data_fixup/move_feature_flags_to_settings.rb:81

                 context.feature_flags.detect { |ff| ff.feature == feature_flag_name.to_s }
               else
                 context.feature_flags.find_by(feature: feature_flag_name.to_s)
               end
    override_value = nil
    locked = true
    if override
      case override.state
      when "allowed"
        # no op
      when "allowed_on"
        override_value = true
        locked = false
      when "on"
        override_value = true
      when "off"
        override_value = false
      else
        Rails.logger.warn("DataFixup::MoveFeatureFlagsToSettings => unable to handle override state for context " \
                          "#{context.asset_string} of feature #{override.id} with state #{override.state}")
      end
    end

    unless override_value.nil?
      if context.is_a?(Account)
        context.settings[setting_name] = inherited ? { locked:, value: override_value } : override_value
      else
        context.settings_frd[setting_name] = override_value
      end
      context.save!
    end
  end
  private_class_method :figure_setting_from_override
end

View on GitHub (pinned to 1c9f0bb801)