hashicorp/nomad · error
task with ID %q already started
Error message
task with ID %q already started
What it means
This error is thrown by the Docker driver's StartTask when a task with the same ID is already registered in the driver's in-memory task store. The driver refuses to start a duplicate task to prevent two Docker containers from being created for one allocation/task ID. It indicates a caller-side lifecycle bug: StartTask was invoked for a task that was never restored or cleaned up.
Source
Thrown at drivers/docker/driver.go:332
go h.run()
return nil
}
func loggingIsEnabled(driverCfg *DriverConfig, taskCfg *drivers.TaskConfig) bool {
if driverCfg.DisableLogCollection {
return false
}
if taskCfg.StderrPath == os.DevNull && taskCfg.StdoutPath == os.DevNull {
return false
}
return true
}
func (d *Driver) StartTask(cfg *drivers.TaskConfig) (*drivers.TaskHandle, *drivers.DriverNetwork, 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 driverConfig.Image == "" {
return nil, nil, fmt.Errorf("image name required for docker driver")
}
driverConfig.Image = strings.TrimPrefix(driverConfig.Image, "https://")
driverConfig.ImagePullTimeout = getValue(driverConfig.ImagePullTimeout, d.config.ImagePullTimeout)
handle := drivers.NewTaskHandle(taskHandleVersion)
handle.Config = cfgView on GitHub (pinned to 482b49bf1a)
Solutions
- Check d.tasks (the in-memory task store) for the task ID and remove the stale entry via the driver's task cleanup/DestroyTask path before restarting
- Verify the caller is not issuing duplicate StartTask calls for the same allocation; dedupe on the client/scheduler side
- Restart the Nomad client or reload the driver plugin to clear the in-memory task map if the task is genuinely gone (e.g. container already removed)
- If the task should be reattached instead of started, use the driver's RecoverTask path rather than StartTask
Example fix
// before
d.tasks.Set(task.ID, handle)
...
// duplicate restart attempt
handle, net, err := driver.StartTask(cfg) // -> already started
// after
if d.tasks.Get(task.ID) != nil { // container handle already exists
if _, err := driver.RecoverTask(&drivers.TaskRecoveryConfig{TaskID: task.ID, Handle: oldHandle}); err != nil {
// handle recovery error
}
} else {
handle, net, err = driver.StartTask(cfg)
} Defensive patterns
Strategy: validation
Validate before calling
// caller-side: ensure task ID not already dispatched to this driver
if driverTaskHandles[cfg.ID] != nil {
return fmt.Errorf("task %q already started; recover instead of start", cfg.ID)
} Type guard
func isTaskAlreadyStartedErr(err error) bool {
return err != nil && strings.Contains(err.Error(), "already started")
} Try / catch
if _, _, err := driver.StartTask(cfg); err != nil {
if isTaskAlreadyStartedErr(err) {
// reattach via RecoverTask instead of retrying StartTask
return driver.RecoverTask(&drivers.TaskRecoveryConfig{TaskID: cfg.ID})
}
return err
} Prevention
- Track task IDs you have dispatched and never call StartTask twice for the same ID
- Use RecoverTask for tasks that may already exist after a client restart
- Always pair StartTask with a corresponding DestroyTask/cleanup on failure paths to avoid stale entries
- Make task-start RPCs idempotent at the caller level (dedupe by allocation ID)
When it happens
Trigger: Calling StartTask with a drivers.TaskConfig whose cfg.ID matches a task previously started (and still tracked) by the same driver instance — e.g. double-dispatch of a task start event, a retry after a partial failure where the task handle was already registered, or re-running StartTask during driver restore/reconnect without the task having been removed.
Common situations: Nomad client restarts where task state was restored but the plugin still tracks the task; client retrying a StartTask RPC after a network timeout even though the first call succeeded; a leaked task from a previous failed StartTask that never reached cleanup; plugins kept alive across allocation updates with stale task entries.
Related errors
- failed to reattach to docker logger process: %v
- failed to launch docker logger plugin: %v
- failed to launch docker logger process %s: %v
- failed to get docker client: %w
- failed to decode driver config: %v
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/743a18b1a41b8b05.
Report an issue: GitHub.