hashicorp/nomad · error
failed to send signal: %w
Error message
failed to send signal: %w
What it means
After successfully parsing the vault change_signal, the hook calls h.lifecycle.Signal(event, signal) to deliver the signal to the running task. If the task driver fails to send the signal (task already exited, driver does not support signaling), handleRenewalFailure returns this wrapped error.
Source
Thrown at client/allocrunner/taskrunner/vault_hook.go:277
if err != nil {
return "", 0, err
}
if err := h.writeToken(token); err != nil {
return "", 0, fmt.Errorf("failed to write Vault token to disk: %w", err)
}
var event *structs.TaskEvent
switch h.vaultBlock.ChangeMode {
case structs.VaultChangeModeSignal:
s, err := signals.Parse(h.vaultBlock.ChangeSignal)
if err != nil {
return "", 0, fmt.Errorf("failed to parse signal: %w", err)
}
event := structs.NewTaskEvent(structs.TaskSignaling).
SetTaskSignal(s).SetDisplayMessage("Vault: new Vault token acquired")
if err := h.lifecycle.Signal(event, h.vaultBlock.ChangeSignal); err != nil {
return "", 0, fmt.Errorf("failed to send signal: %w", err)
}
case structs.VaultChangeModeRestart:
event = structs.NewTaskEvent(structs.TaskRestartSignal).
SetDisplayMessage("Vault: new Vault token acquired")
h.lifecycle.Restart(ctx, event, false)
case structs.VaultChangeModeNoop:
// True to its name, this is a noop!
default:
h.logger.Error("invalid Vault change mode", "mode", h.vaultBlock.ChangeMode)
}
h.updater.updatedVaultToken(token)
return token, time.Second * time.Duration(duration), nil
}
func (h *vaultHook) renewWithBackoff(ctx context.Context, token string) (time.Duration, error) {
var attempts uint64
View on GitHub (pinned to 482b49bf1a)
Solutions
- Check whether the task exited before the signal was sent; fix the task's crash/exit first
- Use change_mode = "restart" instead of "signal" if the driver does not support signaling
- Verify the driver in use implements Signal (e.g. docker/exec do, some raw drivers may not)
- Inspect taskrunner/driver logs at the renewal time for the underlying Signal error
Example fix
// before
vault {
change_mode = "signal"
change_signal = "SIGHUP"
}
// after
vault {
change_mode = "restart"
} Defensive patterns
Strategy: retry
Validate before calling
// ensure the driver supports signaling before using change_mode = "signal"
caps, err := driver.Capabilities()
if err != nil || !caps.Signal { /* use restart mode instead */ } Try / catch
if err := hookSignal(); err != nil {
if strings.Contains(err.Error(), "failed to send signal") {
// task likely exited; wait for restart/retry once
time.Sleep(retryDelay)
return hookSignal()
}
return err
} Prevention
- Use change_mode = "restart" for tasks that don't handle signals
- Keep tasks long-lived if using signal-based token rotation
- Monitor task exits around token renewal windows
- Verify driver Signal support for your task driver
When it happens
Trigger: change_mode = "signal" with a valid change_signal, a new Vault token arrives, and lifecycle.Signal returns an error — typically because the task has exited/is not running, or the driver's Signal implementation rejects the call.
Common situations: Short-lived tasks that finish before token renewal; drivers without signal support being used with change_mode=signal; task crashed and was mid-restart when the renewed token arrived.
Related errors
- failed to parse signal: %w
- no signed workload identity available
- DriverStatsNotImplemented
- ErrMultipleNamespaces
- non-default Vault cluster requires Nomad Enterprise
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/9a8be11137cfef7b.
Report an issue: GitHub.