puppetlabs/puppet · error · ArgumentError

Unable to parse '%{str}' using format '%{format}'

Error message

Unable to parse '%{str}' using format '%{format}'

What it means

Timestamp.parse with a single (non-Array) format String delegates to DateTime._strptime and raises 'Unable to parse ... using format ...' when the result is nil (no match at all) or contains :leftover (the format matched a prefix but trailing text remains). This is the single-format counterpart of error 554.

Source

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

      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?
    end
    fraction = parsed[:sec_fraction]

    # Convert msec rational found in _strptime hash to usec
    fraction *= 1_000_000 unless fraction.nil?

    # Create the Time instance and adjust for timezone

View on GitHub (pinned to e227c27540)

Solutions

  1. Match the format to the actual string: '2023-01-31T12:00:00.123' needs '%FT%T.%N'
  2. Strip the input: str.strip, and remove trailing junk before parsing
  3. Switch to an Array of formats to tolerate variants (the :default set covers common ISO shapes)
  4. Ambiguous day/month order cannot be fixed by formats - enforce an input contract upstream

Example fix

// before
ts = Timestamp.parse(raw, '%F %T')   # raw '2023-01-31T12:00:00.500'

// after
ts = Timestamp.parse(raw.strip, '%FT%T.%N')
Defensive patterns

Strategy: try-catch

Validate before calling

re = /\A\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}:\d{2}(\.\d+)?\z/
raise DataError, "bad shape #{str.inspect}" unless str.to_s.strip.match?(re)
ts = Timestamp.parse(str.strip, '%F %T.%N')

Try / catch

begin
  ts = Timestamp.parse(str, fmt)
rescue ArgumentError => e
  ts = Timestamp.parse(str, :default)   # fall back to the built-in set
  raise DataError, "unparseable #{str.inspect}" if ts.nil?
end

Prevention

When it happens

Trigger: Timestamp.parse('31-01-2023', '%F') (day-first vs year-first), '2023-01-31 12:00:00 extra', '%F' against a string with time when format omits %T, or a format directive the string does not satisfy.

Common situations: Hard-coding one format for heterogeneous input (EU vs US date order); strings with trailing whitespace/units; fractional seconds present but format lacks .%N; ISO8601 with 'Z' or offset parsed by a format without a zone directive.

Understand the failure class

Related errors


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