puppetlabs/puppet · error · ArgumentError

Bad format specifier '%{expression}' in '%{format}', at posi

Error message

Bad format specifier '%{expression}' in '%{format}', at position %{position}

What it means

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.

Source

Thrown at lib/puppet/pops/time/timespan.rb:663

          position += 1
          if state == STATE_LITERAL
            if codepoint == 0x25 # '%'
              state = STATE_PAD
              fstart = position
              padchar = '0'
              width = nil
            else
              append_literal(bld, codepoint)
            end
            next
          end

          case codepoint
          when 0x25 # '%'
            append_literal(bld, codepoint)
            state = STATE_LITERAL
          when 0x2D # '-'
            raise ArgumentError, bad_format_specifier(str, fstart, position) unless state == STATE_PAD

            padchar = nil
            state = STATE_WIDTH
          when 0x5F # '_'
            raise ArgumentError, bad_format_specifier(str, fstart, position) unless state == STATE_PAD

            padchar = ' '
            state = STATE_WIDTH
          when 0x44 # 'D'
            highest = DAY_MAX
            bld << Format::DaySegment.new(padchar, width)
            state = STATE_LITERAL
          when 0x48 # 'H'
            highest = HOUR_MAX unless highest > HOUR_MAX
            bld << Format::HourSegment.new(padchar, width)
            state = STATE_LITERAL
          when 0x4D # 'M'
            highest = MIN_MAX unless highest > MIN_MAX

View on GitHub (pinned to e227c27540)

Solutions

  1. Keep the order strictly: % flag width directive - exactly one of '-', '_', or '0' pad, e.g. '%-5H'
  2. Remove duplicate flags: '%--H' -> '%-H'
  3. Do not use '-' to mean negative values - Timespan.format emits the '-' literal itself for negative durations
  4. Validate the format early with FormatParser.singleton.parse_format(fmt) inside a rescue to surface errors at config-load time

Example fix

// before
span.format('%-5-D')            # second '-' is at a width position -> raise

// after
span.format('%-5D')              # one flag, optional width, then directive
Defensive patterns

Strategy: validation

Validate before calling

# compile once at load; raises here (549/550/551/552) instead of at runtime
VALID_FMT = Puppet::Pops::Time::Timespan::FormatParser.singleton.parse_format('%-5D')

Type guard

def wellformed_flag_order?(fmt)
  # every specifier must be % then optional single flag (- or _) then digits then [DHMSLN]
  fmt.scan(/%([-_]?)(\d*)([DHMSLN])/) { |f, w, d| }.size == fmt.count('%')
end

Try / catch

begin
  span.format(fmt)
rescue ArgumentError => e
  raise ConfigError, "bad format #{fmt.inspect}: #{e.message}"
end

Prevention

When it happens

Trigger: 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.

Common situations: 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).

Related errors


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