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_STOPPEDView on GitHub (pinned to e227c27540)
Solutions
- Re-check the current state right before signalling (`sc query <name>`) and skip the signal if the service is already in the target state.
- 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).
- Run elevated as Administrator so the service handle carries the needed control access rights.
- For ERROR_INVALID_SERVICE_CONTROL, update the service to accept the control (its dispatcher sets dwControlsAccepted) or stop requesting pause/continue for it.
- 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
- Query the state immediately before sending a control signal; never assume the state from earlier checks.
- Treat ERROR_SERVICE_NOT_ACTIVE on stop as idempotent success.
- Only request pause/continue for services whose dwControlsAccepted includes them.
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
- Service query for %{parameter_name} failed
- Unknown Service state '%{current_state}' for '%{service_name
- Unknown start type '%{start_type}' for '%{service_name}'
- Failed to fetch services
- Failed to open a handle to the service
AI-assisted analysis of puppetlabs/puppet@e227c27540 (2026-08-21).
Data as JSON: /api/errors/6ccf7c73f14b24ec.
Report an issue: GitHub.