hashicorp/nomad · error

task with ID %q already started

Error message

task with ID %q already started

What it means

StartTask checks the driver's in-memory task map for cfg.ID before doing any work. If a task with the same ID is already tracked by this driver instance, it refuses to start a duplicate and returns this error. It is a guard against double-starting the same allocation's task.

Source

Thrown at drivers/exec/driver.go:460

		exec:         exec,
		pid:          taskState.Pid,
		pluginClient: pluginClient,
		taskConfig:   taskState.TaskConfig,
		procState:    drivers.TaskStateRunning,
		startedAt:    taskState.StartedAt,
		exitResult:   &drivers.ExitResult{},
		logger:       d.logger,
	}

	d.tasks.Set(taskState.TaskConfig.ID, h)

	go h.run()
	return nil
}

func (d *Driver) StartTask(cfg *drivers.TaskConfig) (handle *drivers.TaskHandle, network *drivers.DriverNetwork, err error) {
	if _, ok := d.tasks.Get(cfg.ID); ok {
		return nil, nil, fmt.Errorf("task with ID %q already started", cfg.ID)
	}

	var driverConfig TaskConfig
	if err := cfg.DecodeDriverConfig(&driverConfig); err != nil {
		return nil, nil, fmt.Errorf("failed to decode driver config: %v", err)
	}

	if err := driverConfig.validate(); err != nil {
		return nil, nil, fmt.Errorf("failed driver config validation: %v", err)
	}

	if cfg.User == "" {
		cfg.User = "nobody"
	}

	d.logger.Debug("setting up user", "user", cfg.User)

	if err := d.userIDValidator.HasValidIDs(cfg.User); err != nil {

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Check task status before starting; call StopTask/DestroyTask on the existing handle if the task should be restarted.
  2. Verify your client code is not retrying StartTask on timeout without checking state first.
  3. Restart the Nomad client to clear leaked in-memory handles if the underlying task is provably dead.

Example fix

// before
handle, _, _ := drv.StartTask(cfg) // retried blindly
// after
if _, ok := drv.InspectTask(cfg.ID); ok {
    _ = drv.StopTask(cfg.ID, 0, "restarting")
    _ = drv.DestroyTask(cfg.ID, false)
}
handle, _, err := drv.StartTask(cfg)
Defensive patterns

Strategy: validation

Validate before calling

// guard caller side before starting
if _, err := drv.InspectTask(cfg.ID); err == nil {
    return fmt.Errorf("task %s already running; stop it first", cfg.ID)
}

Prevention

When it happens

Trigger: Calling StartTask twice with the same drivers.TaskConfig.ID without an intervening StopTask/destroy; a task handle left in d.tasks because a prior StartTask partially failed after registering.

Common situations: Bug in custom tooling that invokes the driver plugin API directly; a retried StartTask RPC after a timeout where the first call actually succeeded; leaked handle after a failed shutdown.

Related errors


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