hashicorp/nomad · error

handle cannot be nil

Error message

handle cannot be nil

What it means

Defensive guard in exec Driver.RecoverTask: a nil *drivers.TaskHandle was passed, which is a programming error by the plugin caller (a real recovery always has a handle); no handle means no task ID to look up.

Source

Thrown at drivers/exec/driver.go:405

		d.setFingerprintFailure()
		return fp
	}

	if cgroupslib.GetMode() == cgroupslib.OFF {
		fp.Health = drivers.HealthStateUnhealthy
		fp.HealthDescription = drivers.NoCgroupMountMessage
		d.setFingerprintFailure()
		return fp
	}

	fp.Attributes["driver.exec"] = pstructs.NewBoolAttribute(true)
	d.setFingerprintSuccess()
	return fp
}

func (d *Driver) RecoverTask(handle *drivers.TaskHandle) error {
	if handle == nil {
		return fmt.Errorf("handle cannot be nil")
	}

	// If already attached to handle there's nothing to recover.
	if _, ok := d.tasks.Get(handle.Config.ID); ok {
		d.logger.Trace("nothing to recover; task already exists",
			"task_id", handle.Config.ID,
			"task_name", handle.Config.Name,
		)
		return nil
	}

	// Handle doesn't already exist, try to reattach
	var taskState TaskState
	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)
	}

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Ensure RecoverTask is only called with a valid handle from drivers.TaskRegistry or StartTask output
  2. Guard the call site with a nil check before invoking
  3. Fix handle construction/serialization upstream so nil handles aren't propagated

Example fix

// before
if err := driver.RecoverTask(nil); err != nil { ... }
// after
if handle == nil {
  return fmt.Errorf("no handle to recover")
}
if err := driver.RecoverTask(handle); err != nil { ... }
Defensive patterns

Strategy: type-guard

Validate before calling

if handle == nil {
  return errors.New("refusing to recover: nil handle")
}

Type guard

func hasHandle(h *drivers.TaskHandle) bool {
  return h != nil && h.Config != nil && h.Config.ID != ""
}

Try / catch

if !hasHandle(handle) {
  return fmt.Errorf("skip recovery: no valid handle")
}
if err := driver.RecoverTask(handle); err != nil {
  return fmt.Errorf("recover failed: %w", err)
}

Prevention

When it happens

Trigger: Calling Driver.RecoverTask(nil), typically from host-side task restoration code paths where a handle failed to deserialize or was not produced by StartTask.

Common situations: Plugin/host integration bugs; custom code paths or tests invoking RecoverTask without a handle; restore logic after agent restart with corrupted handle data.

Related errors


AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04). Data as JSON: /api/errors/892dbec7dea3d6b7. Report an issue: GitHub.