puppetlabs/puppet · error · Puppet::Error

Invalid context to parse: %{context}

Error message

Invalid context to parse: %{context}

What it means

Puppet::Util::SELinuxUtil-class method parse_selinux_context (selinux.rb:94) splits an SELinux context into seluser/selrole/seltype/selrange using the regex /^([^\s:]+):([^\s:]+):([^\s:]+)(?::([\sa-zA-Z0-9:,._-]+))?$/ on a single line. nil and the literal string 'unlabeled' short-circuit to nil (line 91); anything else that lacks at least three non-empty, colon-separated, whitespace-free fields raises Puppet::Error 'Invalid context to parse: <context>'.

Source

Thrown at lib/puppet/util/selinux.rb:94

    # If the file exists we should pass the mode to selabel_lookup for the most specific
    # matching.  If not, we can pass a mode of 0.
    mode = file_mode(file, resource_ensure)

    retval = Selinux.selabel_lookup(handle, file, mode)
    retval == -1 ? nil : retval[1]
  end

  # Take the full SELinux context returned from the tools and parse it
  # out to the three (or four) component parts.  Supports :seluser, :selrole,
  # :seltype, and on systems with range support, :selrange.
  def parse_selinux_context(component, context)
    if context.nil? or context == "unlabeled"
      return nil
    end

    components = /^([^\s:]+):([^\s:]+):([^\s:]+)(?::([\sa-zA-Z0-9:,._-]+))?$/.match(context)
    unless components
      raise Puppet::Error, _("Invalid context to parse: %{context}") % { context: context }
    end

    case component
    when :seluser
      components[1]
    when :selrole
      components[2]
    when :seltype
      components[3]
    when :selrange
      components[4]
    else
      raise Puppet::Error, _("Invalid SELinux parameter type")
    end
  end

  # This updates the actual SELinux label on the file.  You can update
  # only a single component or update the entire context.

View on GitHub (pinned to e227c27540)

Solutions

  1. Handle the documented empty cases yourself first: return early when context.nil? || context == 'unlabeled'.
  2. Take only the context field from tool output (e.g., split and select the token matching user:role:type) rather than the whole line.
  3. Pre-validate with the same regex the library uses: ctx =~ /^([^\s:]+):([^\s:]+):([^\s:]+)(?::([\sa-zA-Z0-9:,._-]+))?$/ before calling.
  4. Rescue Puppet::Error around parse calls and log the raw string to identify which tool produced the malformed context.

Example fix

// before
range = parse_selinux_context(:selrange, raw_ls_z_output) # whole line 'unconfined_u:object_r:user_home_t:s0 file.txt'

// after
line = raw.to_s.lines.first.to_s
ctx = line.split.find { |t| t.count(':') >= 2 && t =~ /\A[^\s:]+:[^\s:]+:[^\s:]+/ }
range = ctx.nil? ? nil : parse_selinux_context(:selrange, ctx)
Defensive patterns

Strategy: validation

Validate before calling

CTX_RX = /\A([^\s:]+):([^\s:]+):([^\s:]+)(?::([\sa-zA-Z0-9:,._-]+))?\z/
def selinux_context_str?(ctx)
  !ctx.nil? && ctx != 'unlabeled' && ctx.match?(CTX_RX)
end

Try / catch

begin
  parse_selinux_context(component, ctx)
rescue Puppet::Error => e
  Puppet.debug("unparseable selinux context #{ctx.inspect}: #{e.message}")
  nil
end

Prevention

When it happens

Trigger: parse_selinux_context(:selrange, 'unconfined_u:unconfined_r') (only two fields); a context with embedded spaces 'system_u : system_r'; a lone placeholder like '?' or '-' returned by tools; a multiline string passed as the context. Note the pattern is per-line: use the first line of `ls -Z`/lgetfilecon output only.

Common situations: File resources on filesystems without SELinux labeling that return odd sentinel strings instead of 'unlabeled'; parsing `ls -Z` or `matchpathcon` output where columns are blank; custom ranges containing unexpected characters not in [a-zA-Z0-9:,._ -].

Related errors


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