{"record":{"id":"743a18b1a41b8b05","repo":"hashicorp/nomad","slug":"task-with-id-q-already-started","errorCode":null,"errorMessage":"task with ID %q already started","messagePattern":"task with ID %q already started","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"drivers/docker/driver.go","lineNumber":332,"sourceCode":"\n\tgo h.run()\n\n\treturn nil\n}\n\nfunc loggingIsEnabled(driverCfg *DriverConfig, taskCfg *drivers.TaskConfig) bool {\n\tif driverCfg.DisableLogCollection {\n\t\treturn false\n\t}\n\tif taskCfg.StderrPath == os.DevNull && taskCfg.StdoutPath == os.DevNull {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (d *Driver) StartTask(cfg *drivers.TaskConfig) (*drivers.TaskHandle, *drivers.DriverNetwork, error) {\n\tif _, ok := d.tasks.Get(cfg.ID); ok {\n\t\treturn nil, nil, fmt.Errorf(\"task with ID %q already started\", cfg.ID)\n\t}\n\n\tvar driverConfig TaskConfig\n\n\tif err := cfg.DecodeDriverConfig(&driverConfig); err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"failed to decode driver config: %v\", err)\n\t}\n\n\tif driverConfig.Image == \"\" {\n\t\treturn nil, nil, fmt.Errorf(\"image name required for docker driver\")\n\t}\n\n\tdriverConfig.Image = strings.TrimPrefix(driverConfig.Image, \"https://\")\n\n\tdriverConfig.ImagePullTimeout = getValue(driverConfig.ImagePullTimeout, d.config.ImagePullTimeout)\n\n\thandle := drivers.NewTaskHandle(taskHandleVersion)\n\thandle.Config = cfg","sourceCodeStart":314,"sourceCodeEnd":350,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/drivers/docker/driver.go#L314-L350","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nd.tasks.Set(task.ID, handle)\n...\n// duplicate restart attempt\nhandle, net, err := driver.StartTask(cfg) // -> already started\n// after\nif d.tasks.Get(task.ID) != nil { // container handle already exists\n    if _, err := driver.RecoverTask(&drivers.TaskRecoveryConfig{TaskID: task.ID, Handle: oldHandle}); err != nil {\n        // handle recovery error\n    }\n} else {\n    handle, net, err = driver.StartTask(cfg)\n}","handlingStrategy":"validation","validationCode":"// caller-side: ensure task ID not already dispatched to this driver\nif driverTaskHandles[cfg.ID] != nil {\n    return fmt.Errorf(\"task %q already started; recover instead of start\", cfg.ID)\n}","typeGuard":"func isTaskAlreadyStartedErr(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"already started\")\n}","tryCatchPattern":"if _, _, err := driver.StartTask(cfg); err != nil {\n    if isTaskAlreadyStartedErr(err) {\n        // reattach via RecoverTask instead of retrying StartTask\n        return driver.RecoverTask(&drivers.TaskRecoveryConfig{TaskID: cfg.ID})\n    }\n    return err\n}","preventionTips":["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)"],"tags":["docker","nomad-driver","task-lifecycle","duplicate-task-id"],"backgroundTag":"task-already-exists","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}