{"record":{"id":"ccd0dd6d2795b671","repo":"puppetlabs/puppet","slug":"bad-format-specifier-expression-in-format","errorCode":null,"errorMessage":"Bad format specifier '%{expression}' in '%{format}', at position %{position}","messagePattern":"Bad format specifier '%(.+?)' in '%(.+?)', at position %(.+?)","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/puppet/pops/time/timespan.rb","lineNumber":663,"sourceCode":"          position += 1\n          if state == STATE_LITERAL\n            if codepoint == 0x25 # '%'\n              state = STATE_PAD\n              fstart = position\n              padchar = '0'\n              width = nil\n            else\n              append_literal(bld, codepoint)\n            end\n            next\n          end\n\n          case codepoint\n          when 0x25 # '%'\n            append_literal(bld, codepoint)\n            state = STATE_LITERAL\n          when 0x2D # '-'\n            raise ArgumentError, bad_format_specifier(str, fstart, position) unless state == STATE_PAD\n\n            padchar = nil\n            state = STATE_WIDTH\n          when 0x5F # '_'\n            raise ArgumentError, bad_format_specifier(str, fstart, position) unless state == STATE_PAD\n\n            padchar = ' '\n            state = STATE_WIDTH\n          when 0x44 # 'D'\n            highest = DAY_MAX\n            bld << Format::DaySegment.new(padchar, width)\n            state = STATE_LITERAL\n          when 0x48 # 'H'\n            highest = HOUR_MAX unless highest > HOUR_MAX\n            bld << Format::HourSegment.new(padchar, width)\n            state = STATE_LITERAL\n          when 0x4D # 'M'\n            highest = MIN_MAX unless highest > MIN_MAX","sourceCodeStart":645,"sourceCodeEnd":681,"githubUrl":"https://github.com/puppetlabs/puppet/blob/e227c27540975c25aa22d533a52424a9d2fc886a/lib/puppet/pops/time/timespan.rb#L645-L681","documentation":"Inside a Timespan format specifier, '-' means 'suppress padding' and is only legal immediately after '%' (state STATE_PAD). If '-' arrives after a width digit or after another flag - e.g. the parser is already in STATE_WIDTH - internal_parse raises 'Bad format specifier ... at position N' with fstart marking where the specifier began.","triggerScenarios":"Formats like '%-5-D' or '%--H' (double flag), '%_-H' ('_' then '-'), or '%5-H' (width then '-'). Each is illegal because only one pad flag may appear, directly after %, and before any width digits.","commonSituations":"Hand-composing a format by combining strftime habits with Puppet's pad flags; copy-paste merging two format fragments that duplicates the '-'; trying to express a negative sign with '-' inside a specifier (the parser handles sign automatically).","solutions":["Keep the order strictly: % flag width directive - exactly one of '-', '_', or '0' pad, e.g. '%-5H'","Remove duplicate flags: '%--H' -> '%-H'","Do not use '-' to mean negative values - Timespan.format emits the '-' literal itself for negative durations","Validate the format early with FormatParser.singleton.parse_format(fmt) inside a rescue to surface errors at config-load time"],"exampleFix":"// before\nspan.format('%-5-D')            # second '-' is at a width position -> raise\n\n// after\nspan.format('%-5D')              # one flag, optional width, then directive","handlingStrategy":"validation","validationCode":"# compile once at load; raises here (549/550/551/552) instead of at runtime\nVALID_FMT = Puppet::Pops::Time::Timespan::FormatParser.singleton.parse_format('%-5D')","typeGuard":"def wellformed_flag_order?(fmt)\n  # every specifier must be % then optional single flag (- or _) then digits then [DHMSLN]\n  fmt.scan(/%([-_]?)(\\d*)([DHMSLN])/) { |f, w, d| }.size == fmt.count('%')\nend","tryCatchPattern":"begin\n  span.format(fmt)\nrescue ArgumentError => e\n  raise ConfigError, \"bad format #{fmt.inspect}: #{e.message}\"\nend","preventionTips":["Memorize the order: % flag width directive - one flag max, immediately after %","Compile formats once at startup to fail fast on typos","Use '-' once ('%-N'); never double flags"],"tags":["puppet","timespan","format-string","parse-error","argument-error"],"backgroundTag":"invalid-format-string","analyzedSha":"e227c27540975c25aa22d533a52424a9d2fc886a","analyzedAt":"2026-08-21T20:49:46.650Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}