puppetlabs/puppet · error · ArgumentError
Unable to parse '%{timespan}' using format '%{format}'
Error message
Unable to parse '%{timespan}' using format '%{format}' What it means
Format#parse first matches the input string against a regexp compiled from the format segments. If Regexp#match returns nil (the overall shape does not fit - wrong separators, missing groups, extra characters), it raises ArgumentError 'Unable to parse ... using format ...'. This is the pre-validation pass; segment-level digit checking (line 566) is a second, separate raise.
Source
Thrown at lib/puppet/pops/time/timespan.rb:558
end
append_value(bld, ns)
end
end
def initialize(format, segments)
@format = format.freeze
@segments = segments.freeze
end
def format(timespan)
bld = timespan.negative? ? '-'.dup : ''.dup
@segments.each { |segment| segment.append_to(bld, timespan) }
bld
end
def parse(timespan)
md = regexp.match(timespan)
raise ArgumentError, _("Unable to parse '%{timespan}' using format '%{format}'") % { timespan: timespan, format: @format } if md.nil?
nanoseconds = 0
md.captures.each_with_index do |group, index|
segment = @segments[index]
next if segment.is_a?(LiteralSegment)
group.lstrip!
raise ArgumentError, _("Unable to parse '%{timespan}' using format '%{format}'") % { timespan: timespan, format: @format } unless group =~ /\A[0-9]+\z/
nanoseconds += segment.nanoseconds(group)
end
Timespan.new(timespan.start_with?('-') ? -nanoseconds : nanoseconds)
end
def to_s
@format
end
View on GitHub (pinned to e227c27540)
Solutions
- Call Timespan.parse(str) with no format first - it tries the built-in DEFAULTS list which covers the common shapes
- Normalize the input before parsing: strip whitespace, replace ',' with '.', strip unit suffixes
- Pass an Array of candidate formats if your Ruby code calls Format directly, and rescue to try the next one
- For human-style durations ('1h30m'), write a small unit-aware parser and build the Timespan from seconds instead of forcing a fixed format
Example fix
// before
span = Timespan.parse(input, '%H:%M:%S') # input '1h 30m' -> raise
// after
input = input.to_s.strip.gsub(',', '.')
begin
span = Timespan.parse(input)
rescue ArgumentError
span = Timespan(0, parse_human_duration(input))
end Defensive patterns
Strategy: try-catch
Validate before calling
# optional cheap pre-check: only digits, ':', '-', '.', spaces
return DataError.new("malformed duration #{str}") unless str.to_s.match?(/\A[-0-9: .]+\z/)
span = Timespan.parse(str) # defaults tolerate common shapes Try / catch
begin
span = Timespan.parse(str, fmt)
rescue ArgumentError
span = Timespan.parse(str) # fall back to default formats
raise DataError, "unparseable duration #{str.inspect}" if span.nil?
end Prevention
- Call Timespan.parse(str) without a format first - the DEFAULTS cover common shapes
- strip and gsub(',', '.') inputs from users before parsing
- Document the exact accepted grammar next to every user-facing duration parameter
When it happens
Trigger: Timespan.parse('10-30', '%H:%M:%S') (dash vs colon), Timespan.parse('12', '%D-%H') (missing hours), Timespan.parse('1h30m', '%H:%M') (non-spec separators), or passing a default-format string to a custom-format call. Any structural mismatch between input and format raises here.
Common situations: User-supplied duration strings in module parameters ('90 mins', '1h', 'PT1H') fed to a fixed %H:%M format; locale differences (comma decimal separator); trailing whitespace or units in the input; empty string input.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Bad format specifier '%{expression}' in '%{format}', at posi
- Format specifiers %L and %N denotes fractions and must be us
- Format must be a String
- Unable to parse '%{str}' using any of the formats %{formats}
- Unable to parse '%{str}' using format '%{format}'
AI-assisted analysis of puppetlabs/puppet@e227c27540 (2026-08-21).
Data as JSON: /api/errors/30e3e1cafc85f7f1.
Report an issue: GitHub.