puppetlabs/puppet · error · ArgumentError

field 'data_provider' must be a string

Error message

field 'data_provider' must be a string

What it means

The non-String branch of Puppet::ModuleTool::Metadata#validate_data_provider: the legacy 'data_provider' field in metadata.json must be a string, and anything else — number, boolean, array, object, null-as-value — raises ArgumentError at line 216. It is a type check rather than a format check.

Source

Thrown at lib/puppet/module_tool/metadata.rb:216

      raise ArgumentError, _("Invalid 'version' field in metadata.json: %{err}") % { err: err }
    end

    # Validates that the given _value_ is a symbolic name that starts with a letter
    # and then contains only letters, digits, or underscore. Will raise an ArgumentError
    # if that's not the case.
    #
    # @param value [Object] The value to be tested
    def validate_data_provider(value)
      if value.is_a?(String)
        unless value =~ /^[a-zA-Z][a-zA-Z0-9_]*$/
          if value =~ /^[a-zA-Z]/
            raise ArgumentError, _("field 'data_provider' contains non-alphanumeric characters")
          else
            raise ArgumentError, _("field 'data_provider' must begin with a letter")
          end
        end
      else
        raise ArgumentError, _("field 'data_provider' must be a string")
      end
    end

    # Validates that the version range can be parsed by Semantic.
    def validate_version_range(version_range)
      SemanticPuppet::VersionRange.parse(version_range)
    rescue ArgumentError => e
      raise ArgumentError, _("Invalid 'version_range' field in metadata.json: %{err}") % { err: e }
    end
  end
end

View on GitHub (pinned to e227c27540)

Solutions

  1. Quote the value so it is a JSON string: "data_provider": "hiera"
  2. If the field is meaningless for your module, delete it
  3. Lint the file: `python -m json.tool metadata.json` or a JSON schema validator to catch type mistakes

Example fix

// before - metadata.json
"data_provider": 42,

// after
"data_provider": "hiera",
Defensive patterns

Strategy: type-guard

Validate before calling

value = JSON.parse(File.read('metadata.json'))['data_provider']
abort "data_provider must be a string, got #{value.class}" unless value.nil? || value.is_a?(String)

Type guard

# Ruby predicate acting as a type guard before parsing
module_provider_string?(v) = v.is_a?(String) && v.match?(/\A[a-zA-Z][a-zA-Z0-9_]*\Z/)

# usage
raise ArgumentError, 'data_provider must be a string' unless module_data_provider.nil? || module_provider_string?(module_data_provider)

Try / catch

begin
  Puppet::ModuleTool::Metadata.from_hash('data_provider' => raw)
rescue ArgumentError => e
  raise unless e.message =~ /data_provider.*must be a string/
  raw = raw.to_s  # coerce numbers/booleans from loose templates, then retry
  retry
end

Prevention

When it happens

Trigger: A metadata.json containing "data_provider": 42, "data_provider": true, or "data_provider": ["hiera"] — the is_a?(String) test fails and line 216 raises.

Common situations: Generator templates interpolating a Ruby symbol or integer unquoted into JSON; YAML config reused as JSON with unquoted values; copy-paste from documentation showing enum-like values.

Related errors


AI-assisted analysis of puppetlabs/puppet@e227c27540 (2026-08-21). Data as JSON: /api/errors/54b5e329819aaa1d. Report an issue: GitHub.