puppetlabs/puppet · error · Puppet::Error

Could not #{action} #{name}: #{output}

Error message

Could not #{action} #{name}: #{output}

What it means

The systemd provider routes enable/disable/mask/unmask through systemctl_change_enable, which runs systemctl <action> -- <name> and converts any raised exception into Puppet::Error (the interpolated output is nil on the failure path because the assignment never completed). An ensure block always clears @cached_enabled, since post-command state is not guaranteed and a stale cache would corrupt the provider's believed state.

Source

Thrown at lib/puppet/provider/service/systemd.rb:77

        true
      end
    when 'indirect'
      Puppet.debug("Service #{@resource[:name]} is in 'indirect' state and cannot be enabled/disabled")
      true
    else
      current == @resource[:enable]
    end
  end

  # This helper ensures that the enable state cache is always reset
  # after a systemctl enable operation. A particular service state is not guaranteed
  # after such an operation, so the cache must be emptied to prevent inconsistencies
  # in the provider's believed state of the service and the actual state.
  # @param action [String,Symbol] One of 'enable', 'disable', 'mask' or 'unmask'
  def systemctl_change_enable(action)
    output = systemctl(action, '--', @resource[:name])
  rescue => e
    raise Puppet::Error, "Could not #{action} #{name}: #{output}", e.backtrace
  ensure
    @cached_enabled = nil
  end

  def disable
    systemctl_change_enable(:disable)
  end

  def get_start_link_count
    # Start links don't include '.service'. Just search for the service name.
    if @resource[:name] =~ /\.service/
      link_name = @resource[:name].split('.')[0]
    else
      link_name = @resource[:name]
    end

    Dir.glob("/etc/rc*.d/S??#{link_name}").length
  end

View on GitHub (pinned to e227c27540)

Solutions

  1. Reproduce exactly: systemctl enable -- <name> and read its stderr (usually 'Unit <name>.service does not exist')
  2. Order unit installation first, and add a daemon-reload exec with refreshonly between the unit file and the service resource
  3. Confirm the unit is visible: systemctl list-unit-files | grep <name> and systemctl cat <name>
  4. In containers/chroots, don't manage systemd enablement — skip the service resource or run puppet on the host, not inside the namespace

Example fix

# before
file { '/etc/systemd/system/myapp.service': source => 'puppet:///modules/app/myapp.service' }
service { 'myapp': ensure => running, enable => true }

# after
file { '/etc/systemd/system/myapp.service':
  source  => 'puppet:///modules/app/myapp.service',
  notify  => Exec['myapp-daemon-reload'],
}
exec { 'myapp-daemon-reload':
  command     => '/usr/bin/systemctl daemon-reload',
  refreshonly => true,
}
-> service { 'myapp': ensure => running, enable => true }
Defensive patterns

Strategy: validation

Validate before calling

# the unit must exist and be loaded before enable/disable/mask
systemctl cat "${name}" >/dev/null 2>&1 || echo "unit missing — install it and 'systemctl daemon-reload' first"
systemctl list-unit-files | grep -q "^${name}" || echo "unit file not registered; daemon-reload required"

Try / catch

begin
  provider.enable
rescue Puppet::Error => e
  # output is nil on this path; run 'systemctl enable -- #{name}' by hand for the real error
  warn "systemctl enable failed for #{name}: #{e.message}"
end

Prevention

When it happens

Trigger: systemctl enable/disable/mask/unmask exiting non-zero: the unit file does not exist (enable on a never-installed unit), the unit was just installed but systemd was not daemon-reloaded, running inside a container/chroot where systemd is not PID 1, or a read-only /etc (read-only bind mounts, atomic/immutable hosts).

Common situations: Missing Package/File -> Service ordering so enable runs before the unit lands; modules dropping unit files without a systemctl daemon-reload; docker exec / chroot puppet runs; /usr or /etc mounted read-only (Silverblue-style) blocking symlink creation in /etc/systemd/system.

Related errors


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