hashicorp/nomad · error

error: handle cannot be nil

Error message

error: handle cannot be nil

What it means

Returned by the QEMU driver's RecoverTask when called with a nil task handle; recovery cannot restore state for a missing/stale handle, indicating the task ID is unknown after a restart.

Source

Thrown at drivers/qemu/driver.go:288

	matches := versionRegex.FindStringSubmatch(out)
	if len(matches) != 2 {
		fingerprint.Health = drivers.HealthStateUndetected
		fingerprint.HealthDescription = fmt.Sprintf("Failed to parse qemu version from %v", out)
		return fingerprint
	}
	currentQemuVersion := matches[1]

	fingerprint.Attributes[driverAttr] = pstructs.NewBoolAttribute(true)
	fingerprint.Attributes[driverVersionAttr] = pstructs.NewStringAttribute(currentQemuVersion)
	fingerprint.Attributes[driverEmulatorsAttr] = pstructs.NewStringAttribute(strings.Join(emulators, ","))

	return fingerprint
}

func (d *Driver) RecoverTask(handle *drivers.TaskHandle) error {
	if handle == nil {
		return fmt.Errorf("error: 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
	}

	var taskState TaskState
	if err := handle.GetDriverState(&taskState); err != nil {
		d.logger.Error("failed to decode taskConfig state from handle", "error", err, "task_id", handle.Config.ID)
		return fmt.Errorf("failed to decode taskConfig state from handle: %v", err)
	}

	plugRC, err := pstructs.ReattachConfigToGoPlugin(taskState.ReattachConfig)

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Ensure the caller obtains a valid *drivers.TaskHandle (via drivers.NewTaskHandle + Config) before calling RecoverTask.
  2. Add a nil check at the call site and skip/log instead of calling RecoverTask with nil.
  3. If handles come from persisted state, verify that state was successfully loaded before recovery.

Example fix

// before
err := d.RecoverTask(h)
// after
if h == nil {
    return errors.New("no task handle to recover")
}
err := d.RecoverTask(h)
Defensive patterns

Strategy: type-guard

Validate before calling

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

Type guard

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

Try / catch

if !recoverable(h) {
    return errors.New("no valid handle to recover")
}
err := d.RecoverTask(h)

Prevention

When it happens

Trigger: Calling Driver.RecoverTask(nil), e.g. when a caller's handle lookup failed but nil was passed through instead of being checked.

Common situations: Client plugin re-attachment logic passing an unpopulated handle after a failed GetTaskHandle; tests invoking RecoverTask without constructing a handle.

Related errors


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