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
- Check task status before starting; call StopTask/DestroyTask on the existing handle if the task should be restarted.
- Verify your client code is not retrying StartTask on timeout without checking state first.
- 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
- Track StartTask calls in a map/set keyed by task ID in your orchestration code.
- On StartTask timeout, check task state before retrying instead of blindly re-calling.
- Always pair StartTask with StopTask/DestroyTask in cleanup paths.
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
- can't set health of allocations for a terminal deployment
- can't unblock running deployment
- default_pid_mode must be %q or %q, got %q
- default_ipc_mode must be %q or %q, got %q
- allow_caps configured with capabilities not supported by sys
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/15f1b58841a0bfd1.
Report an issue: GitHub.