puppetlabs/puppet · error · ArgumentError

Unable to parse '%{str}' using any of the formats %{formats}

Error message

Unable to parse '%{str}' using any of the formats %{formats}

What it means

Timestamp.parse with an Array of formats (or :default, which expands to the DEFAULT_FORMATS array) tries each format via DateTime._strptime; a candidate is discarded when it returns nil, leaves :leftover (trailing unparsed text), or - when an explicit timezone argument was given - the parsed hash contains :zone. If every candidate is exhausted, ArgumentError 'Unable to parse ... using any of the formats ...' lists the joined formats.

Source

Thrown at lib/puppet/pops/time/timestamp.rb:87

    has_timezone = !(timezone.nil? || timezone.empty? || timezone == :default)
    if format.nil? || format == :default
      format = has_timezone ? DEFAULT_FORMATS_WO_TZ : DEFAULT_FORMATS
    end

    parsed = nil
    if format.is_a?(Array)
      format.each do |fmt|
        parsed = DateTime._strptime(str, fmt)
        next if parsed.nil?

        if parsed.include?(:leftover) || (has_timezone && parsed.include?(:zone))
          parsed = nil
          next
        end
        break
      end
      if parsed.nil?
        raise ArgumentError, _(
          "Unable to parse '%{str}' using any of the formats %{formats}"
        ) % { str: str, formats: format.join(', ') }
      end
    else
      parsed = DateTime._strptime(str, format)
      if parsed.nil? || parsed.include?(:leftover)
        raise ArgumentError, _("Unable to parse '%{str}' using format '%{format}'") % { str: str, format: format }
      end

      if has_timezone && parsed.include?(:zone)
        raise ArgumentError, _(
          'Using a Timezone designator in format specification is mutually exclusive to providing an explicit timezone argument'
        )
      end
    end
    unless has_timezone
      timezone = parsed[:zone]
      has_timezone = !timezone.nil?

View on GitHub (pinned to e227c27540)

Solutions

  1. Pass your own Array of formats covering the real shapes: Timestamp.parse(str, ['%d/%m/%Y %T', '%m/%d/%Y %T'])
  2. Normalize the string first - gsub('/', '-'), strip trailing zone names or whitespace
  3. If you supply a timezone argument, drop %Z/%z from the candidate formats (the :zone rejection rule)
  4. Pre-validate with a cheap regex per accepted shape before calling parse

Example fix

// before
ts = Puppet::Pops::Time::Timestamp.parse(api_date)   # '2023/01/31 12:00:00' -> raise listing defaults

// after
ts = Puppet::Pops::Time::Timestamp.parse(
  api_date.gsub('/', '-'),
  ['%F %T', '%FT%T', '%F %T.%N', '%F']
)
Defensive patterns

Strategy: try-catch

Validate before calling

SHAPES = [/\A\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:?\d{2})?\z/, /\A\d{4}-\d{2}-\d{2}( \d{2}:\d{2}:\d{2})?\z/]
raise DataError, "unrecognized timestamp shape" unless SHAPES.any? { |r| str.match?(r) }

Try / catch

begin
  ts = Timestamp.parse(str, formats)
rescue ArgumentError => e
  raise DataError, "input #{str.inspect} matches none of #{formats.join(', ')}"
end

Prevention

When it happens

Trigger: Timestamp.parse('2023-13-45T99:99') against defaults (out-of-range fields), '2023/01/31' with slash separators against %F dash formats, '2023-01-31 12:00 hello' (leftover), or a zone-bearing string plus an explicit timezone argument (each candidate rejected via the :zone rule).

Common situations: Feeding log lines, database dumps, or third-party API timestamps with unexpected separators or precision into Timestamp()/Timestamp.parse; DST-ambiguous strings; inputs with trailing timezone names while a timezone arg was also supplied.

Understand the failure class

Related errors


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