puppetlabs/puppet · error · Puppet::Error

Numeric modes must be in octal, not decimal!

Error message

Numeric modes must be in octal, not decimal!

What it means

Raised by symbolic_mode_to_int when the mode is an all-digit string that contains digits outside octal range (8 or 9). The check runs after the pure-octal pattern ^[0-7]+$ fails but ^\d+$ succeeds, so Puppet knows the value is numeric-but-decimal-looking and rejects it, matching chmod semantics.

Source

Thrown at lib/puppet/util/symbolic_file_mode.rb:57

    # up the bits with values 4, 2, and 1. Omitted digits are assumed to be
    # leading zeros."
    case value
    when Numeric
      value.to_s(8)
    when /^0?[0-7]{1,4}$/
      value.to_i(8).to_s(8) # strip leading 0's
    else
      value
    end
  end

  def symbolic_mode_to_int(modification, to_mode = 0, is_a_directory = false)
    if modification.nil? or modification == ''
      raise Puppet::Error, _("An empty mode string is illegal")
    elsif modification =~ /^[0-7]+$/
      return modification.to_i(8)
    elsif modification =~ /^\d+$/
      raise Puppet::Error, _("Numeric modes must be in octal, not decimal!")
    end

    fail _("non-numeric current mode (%{mode})") % { mode: to_mode.inspect } unless to_mode.is_a?(Numeric)

    original_mode = {
      's' => (to_mode & 0o7000) >> 9,
      'u' => (to_mode & 0o0700) >> 6,
      'g' => (to_mode & 0o0070) >> 3,
      'o' => (to_mode & 0o0007) >> 0,
      # Are there any execute bits set in the original mode?
      'any x?' => (to_mode & 0o0111) != 0
    }
    final_mode = {
      's' => original_mode['s'],
      'u' => original_mode['u'],
      'g' => original_mode['g'],
      'o' => original_mode['o'],
    }

View on GitHub (pinned to e227c27540)

Solutions

  1. Rewrite the mode using octal digits only, e.g. '0644' or '0755'.
  2. Always quote mode values in manifests so they are not treated as decimal Integers.
  3. Check the value for 8/9 digits and typos in the copied source.

Example fix

# before
file { '/tmp/x': mode => '648' } # 8 is not an octal digit

# after
file { '/tmp/x': mode => '0644' }
Defensive patterns

Strategy: validation

Validate before calling

s = mode.to_s
unless s.empty? || s =~ /^[0-7]+$/ || s =~ /^([ugoa]*)([-+=].*)$/
  raise ArgumentError, "mode #{s.inspect} must be octal digits or symbolic"
end
Puppet::Util::SymbolicMode.symbolic_mode_to_int(s)

Type guard

def octal_or_symbolic?(m)
  m = m.to_s
  !m.empty? && (m =~ /^[0-7]+$/ || m =~ /^([ugoa]*)([-+=].*)$/) ? true : false
end

Prevention

When it happens

Trigger: symbolic_mode_to_int('0999') or a manifest with mode => '648' — any digit string containing 8 or 9. Related footgun: unquoted bare integers like mode => 644 are parsed as decimal by Ruby before reaching this code.

Common situations: Copy-pasted Windows-style or arbitrary numbers into mode attributes; confusion between '644' and '0644' (both legal) versus values containing 8/9; generating mode strings from arithmetic.

Related errors


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