{"record":{"id":"30e3e1cafc85f7f1","repo":"puppetlabs/puppet","slug":"unable-to-parse-timespan-using-format-form","errorCode":null,"errorMessage":"Unable to parse '%{timespan}' using format '%{format}'","messagePattern":"Unable to parse '%(.+?)' using format '%(.+?)'","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/puppet/pops/time/timespan.rb","lineNumber":558,"sourceCode":"          end\n          append_value(bld, ns)\n        end\n      end\n\n      def initialize(format, segments)\n        @format = format.freeze\n        @segments = segments.freeze\n      end\n\n      def format(timespan)\n        bld = timespan.negative? ? '-'.dup : ''.dup\n        @segments.each { |segment| segment.append_to(bld, timespan) }\n        bld\n      end\n\n      def parse(timespan)\n        md = regexp.match(timespan)\n        raise ArgumentError, _(\"Unable to parse '%{timespan}' using format '%{format}'\") % { timespan: timespan, format: @format } if md.nil?\n\n        nanoseconds = 0\n        md.captures.each_with_index do |group, index|\n          segment = @segments[index]\n          next if segment.is_a?(LiteralSegment)\n\n          group.lstrip!\n          raise ArgumentError, _(\"Unable to parse '%{timespan}' using format '%{format}'\") % { timespan: timespan, format: @format } unless group =~ /\\A[0-9]+\\z/\n\n          nanoseconds += segment.nanoseconds(group)\n        end\n        Timespan.new(timespan.start_with?('-') ? -nanoseconds : nanoseconds)\n      end\n\n      def to_s\n        @format\n      end\n","sourceCodeStart":540,"sourceCodeEnd":576,"githubUrl":"https://github.com/puppetlabs/puppet/blob/e227c27540975c25aa22d533a52424a9d2fc886a/lib/puppet/pops/time/timespan.rb#L540-L576","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nspan = Timespan.parse(input, '%H:%M:%S')   # input '1h 30m' -> raise\n\n// after\ninput = input.to_s.strip.gsub(',', '.')\nbegin\n  span = Timespan.parse(input)\nrescue ArgumentError\n  span = Timespan(0, parse_human_duration(input))\nend","handlingStrategy":"try-catch","validationCode":"# optional cheap pre-check: only digits, ':', '-', '.', spaces\nreturn DataError.new(\"malformed duration #{str}\") unless str.to_s.match?(/\\A[-0-9: .]+\\z/)\nspan = Timespan.parse(str)   # defaults tolerate common shapes","typeGuard":null,"tryCatchPattern":"begin\n  span = Timespan.parse(str, fmt)\nrescue ArgumentError\n  span = Timespan.parse(str)   # fall back to default formats\n  raise DataError, \"unparseable duration #{str.inspect}\" if span.nil?\nend","preventionTips":["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"],"tags":["puppet","timespan","parse-error","format-string","argument-error"],"backgroundTag":"datetime-parse-failed","analyzedSha":"e227c27540975c25aa22d533a52424a9d2fc886a","analyzedAt":"2026-08-21T20:49:46.650Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}