instructure/canvas-lms · error · ArgumentError

invalid scope for hash

Error message

invalid scope for hash

What it means

CustomData#hash_data_from_scope walks a '/'-delimited scope through a nested hash and raises ArgumentError as soon as an intermediate value is not a Hash. get_data uses it, so reading a scope whose path points into (or through) a non-hash value fails.

Solutions

  1. Correct the scope string so every intermediate segment corresponds to a nested hash.
  2. Delete the conflicting leaf data and re-store it with the correct nested shape via set_data.
  3. Before reading, call get_data on the parent scope and verify it is a Hash.

Example fix

// before
data = custom_data.get_data(user, 'profile', 'settings/theme/colors')
// after
settings = custom_data.get_data(user, 'profile', 'settings')
raise ArgumentError, 'settings is not a hash' unless settings.is_a?(Hash)
data = settings.dig('theme', 'colors')
Defensive patterns

Strategy: validation

Validate before calling

parent = custom_data.get_data(user, namespace, 'settings')
raise ArgumentError, 'settings is not a hash' unless parent.is_a?(Hash)

Type guard

def hash_at_path?(data, scope) = scope.split('/').inject(data) { |h, k| h.is_a?(Hash) ? h[k] : nil }.is_a?(Hash)

Try / catch

begin
  data = custom_data.get_data(user, namespace, scope)
rescue ArgumentError => e
  Rails.logger.warn("bad custom_data scope #{scope}: #{e.message}")
  data = nil
end

Prevention

When it happens

Trigger: Calling get_data(user, namespace, scope) where the scope path traverses a key that was previously set to a scalar (e.g. set_data with scope 'a/b' then get_data with scope 'a/b/c').

Common situations: Scope schema drift: a value once stored as a leaf string is now treated as a nested namespace; typos in scope strings; different app versions writing conflicting shapes under one namespace.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at app/models/custom_data.rb:72

      yield
      destroyed? || save
    end
  end

  def set_data(scope, val)
    set_hash_data_from_scope(data_json, "d/#{scope}", val)
  end

  def delete_data(scope)
    delete_hash_data_from_scope(data_json, "d/#{scope}")
  end

  private

  def hash_data_from_scope(hash, scope)
    keys = scope.split("/")
    keys.inject(hash) do |h, k|
      raise ArgumentError, "invalid scope for hash" unless h.is_a?(Hash)

      h[k]
    end
  end

  def set_hash_data_from_scope(hash, scope, data)
    keys = scope.split("/")
    last = keys.pop

    traverse = lambda do |hsh, key_idx|
      return hsh if key_idx == keys.length

      k = keys[key_idx]
      h = hsh[k]
      if h.nil?
        hsh[k] = {}
      elsif !h.is_a? Hash
        raise WriteConflict.new({

View on GitHub (pinned to 1c9f0bb801)