hashicorp/nomad · error
handle cannot be nil
Error message
handle cannot be nil
What it means
The mock driver's RecoverTask restores a task from a drivers.TaskHandle persisted across agent restarts. A nil handle carries no state to restore, so the driver rejects the call immediately. This is a defensive check against a programming error in the caller (usually the client/runtime), not an environment problem.
Source
Thrown at drivers/mock/driver.go:361
if !d.shutdownFingerprintTime.IsZero() && time.Now().After(d.shutdownFingerprintTime) {
health = drivers.HealthStateUndetected
desc = "disabled"
} else {
health = drivers.HealthStateHealthy
attrs["driver.mock_driver"] = pstructs.NewBoolAttribute(true)
desc = drivers.DriverHealthy
}
return &drivers.Fingerprint{
Attributes: attrs,
Health: health,
HealthDescription: desc,
}
}
func (d *Driver) RecoverTask(handle *drivers.TaskHandle) error {
if handle == nil {
return fmt.Errorf("handle cannot be nil")
}
// Unmarshall the driver state and create a new handle
var taskState MockTaskState
if err := handle.GetDriverState(&taskState); err != nil {
d.logger.Error("failed to decode task state from handle", "error", err, "task_id", handle.Config.ID)
return fmt.Errorf("failed to decode task state from handle: %v", err)
}
taskState.Command.parseDurations()
if taskState.ExecCommand != nil {
taskState.ExecCommand.parseDurations()
}
// Correct the run_for time based on how long it has already been running
now := time.Now()
if !taskState.StartedAt.IsZero() {
taskState.Command.runForDuration = taskState.Command.runForDuration - now.Sub(taskState.StartedAt)View on GitHub (pinned to 482b49bf1a)
Solutions
- Ensure the caller obtains the handle from the driver's task store/persisted state and only calls RecoverTask with a non-nil *drivers.TaskHandle.
- Guard the call site: skip or log when handle == nil before invoking RecoverTask.
- If this happens during agent restore, check that task state was correctly persisted and restored upstream.
Example fix
// before
if err := d.RecoverTask(handles[i]); err != nil {...}
// after
if handles[i] == nil {
continue // nothing to recover
}
if err := d.RecoverTask(handles[i]); err != nil {...} Defensive patterns
Strategy: validation
Validate before calling
if handle == nil {
return errors.New("cannot recover task: handle is nil")
}
err := driver.RecoverTask(handle) Type guard
func recoverable(h *drivers.TaskHandle) bool { return h != nil && h.Config != nil } Try / catch
if err := d.RecoverTask(h); err != nil {
if err.Error() == "handle cannot be nil" {
return errors.New("caller bug: nil handle passed to RecoverTask")
}
return err
} Prevention
- Only pass handles obtained from the driver or a properly restored task store.
- Nil-check handles at restore/reconnect boundaries before calling RecoverTask.
- Cover recovery paths with unit tests that exercise nil/empty handle inputs.
When it happens
Trigger: Calling Driver.RecoverTask(nil) on drivers/mock_driver, e.g. during plugin reattach/restore when the handle was never created or was lost and not re-checked for nil.
Common situations: Custom Nomad forks or test harnesses restoring drivers after agent restart with a corrupt/empty task store; unit tests invoking RecoverTask directly with a nil handle.
Related errors
- failed to decode task state from handle: %v
- no exec command is configured
- failed to decode task state from handle: %v
- failed to build ReattachConfig from task state: %v
- failed to reattach to executor: %v
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/ad02cf2f458989c2.
Report an issue: GitHub.