puppetlabs/puppet · error · Puppet::Util::Windows::Error

Failed to send the %{control_signal} signal to the service.

Error message

Failed to send the %{control_signal} signal to the service. Its current state is %{current_state}. Reason for failure:

What it means

Raised by send_service_control_signal when the Win32 ControlService call fails to deliver a control code (stop, pause, continue) to a service. The message includes the signal name from SERVICE_CONTROL_SIGNALS and the service's current state from SERVICE_STATES at the moment of failure, plus the GetLastError reason appended by Puppet::Util::Windows::Error. It is a plain wrapper failure of the SCM control channel, not a timeout.

Source

Thrown at lib/puppet/util/windows/service.rb:574

      # @param [String] service_name name of service
      # @param [Bool] delayed whether the service should be started with a delay or not
      def set_startup_mode_delayed(service_name, delayed)
        delayed_start = SERVICE_DELAYED_AUTO_START_INFO.new
        delayed_start[:fDelayedAutostart] = delayed
        set_optional_parameter(service_name, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, delayed_start)
      end
      private :set_startup_mode_delayed

      # @api private
      # Sends a service control signal to a service
      #
      # @param [:handle] service handle to the service
      # @param [Integer] signal the service control signal to send
      def send_service_control_signal(service, signal)
        FFI::MemoryPointer.new(SERVICE_STATUS.size) do |status_ptr|
          status = SERVICE_STATUS.new(status_ptr)
          if ControlService(service, signal, status) == FFI::WIN32_FALSE
            raise Puppet::Util::Windows::Error, _("Failed to send the %{control_signal} signal to the service. Its current state is %{current_state}. Reason for failure:") % { control_signal: SERVICE_CONTROL_SIGNALS[signal], current_state: SERVICE_STATES[status[:dwCurrentState]] }
          end
        end
      end

      # @api private
      # Waits for a service to transition from one state to
      # another state.
      #
      # @param [:handle] service handle to the service to wait on
      # @param [Integer] initial_state the state that the service is transitioning from.
      # @param [Integer] final_state the state that the service is transitioning to
      # @param [Integer] timeout the minumum number of seconds to wait before timing out
      def wait_on_state_transition(service, initial_state, final_state, timeout)
        # Get the pending state for this transition. Note that SERVICE_RUNNING
        # has two possible pending states, which is why we need this logic.
        if final_state != SERVICE_RUNNING
          pending_state = FINAL_STATES.key(final_state)
        elsif initial_state == SERVICE_STOPPED

View on GitHub (pinned to e227c27540)

Solutions

  1. Re-check the current state right before signalling (`sc query <name>`) and skip the signal if the service is already in the target state.
  2. If the message says current state is 'stopped' and the signal is 'SERVICE_CONTROL_STOP', make the resource idempotent (Puppet normally pre-checks via query_status; verify a custom type does too).
  3. Run elevated as Administrator so the service handle carries the needed control access rights.
  4. For ERROR_INVALID_SERVICE_CONTROL, update the service to accept the control (its dispatcher sets dwControlsAccepted) or stop requesting pause/continue for it.
  5. Retry once after a short delay if the service was in a pending state (the SCM can reject controls mid-transition).

Example fix

# before
Service.stop(service_name) # internally: ControlService(svc, SERVICE_CONTROL_STOP, status)

# after - only signal when the service reports a state that accepts it
Puppet::Util::Windows::Service.query_status(service_handle) do |status|
  state = status[:dwCurrentState]
  break if state == Puppet::Util::Windows::Service::SERVICE_STOPPED
  Service.stop(service_name)
end
Defensive patterns

Strategy: try-catch

Validate before calling

# check the service's current state and accepted controls before signalling
Puppet::Util::Windows::Service.query_status(svc_handle) do |st|
  return if st[:dwCurrentState] == SERVICE_STOPPED # nothing to stop
end

Try / catch

begin
  Service.stop(name)
rescue Puppet::Util::Windows::Error => e
  case e.code
  when 1062 then nil # ERROR_SERVICE_NOT_ACTIVE: already stopped, treat as success
  when 400 then raise "service does not accept this control" # ERROR_INVALID_SERVICE_CONTROL
  else raise
  end
end

Prevention

When it happens

Trigger: Sending SERVICE_CONTROL_STOP to a service already in STOPPED state (ERROR_SERVICE_NOT_ACTIVE); sending a control the service does not accept in its accepted-controls mask (ERROR_INVALID_SERVICE_CONTROL); ControlService on a service in a transition-pending state; ERROR_ACCESS_DENIED when the handle lacks SERVICE_PAUSE_CONTINUE/SERVICE_STOP access.

Common situations: Puppet's ensure => stopped racing with a service that stops itself (or a previous Puppet run) so the STOP arrives when the state is already stopped; manifest declaring pause/continue for a service that never called SetServiceStatus with those accepted controls; running non-elevated against services with restrictive DACLs.

Related errors


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