{"record":{"id":"892dbec7dea3d6b7","repo":"hashicorp/nomad","slug":"handle-cannot-be-nil","errorCode":null,"errorMessage":"handle cannot be nil","messagePattern":"handle cannot be nil","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"drivers/exec/driver.go","lineNumber":405,"sourceCode":"\t\td.setFingerprintFailure()\n\t\treturn fp\n\t}\n\n\tif cgroupslib.GetMode() == cgroupslib.OFF {\n\t\tfp.Health = drivers.HealthStateUnhealthy\n\t\tfp.HealthDescription = drivers.NoCgroupMountMessage\n\t\td.setFingerprintFailure()\n\t\treturn fp\n\t}\n\n\tfp.Attributes[\"driver.exec\"] = pstructs.NewBoolAttribute(true)\n\td.setFingerprintSuccess()\n\treturn fp\n}\n\nfunc (d *Driver) RecoverTask(handle *drivers.TaskHandle) error {\n\tif handle == nil {\n\t\treturn fmt.Errorf(\"handle cannot be nil\")\n\t}\n\n\t// If already attached to handle there's nothing to recover.\n\tif _, ok := d.tasks.Get(handle.Config.ID); ok {\n\t\td.logger.Trace(\"nothing to recover; task already exists\",\n\t\t\t\"task_id\", handle.Config.ID,\n\t\t\t\"task_name\", handle.Config.Name,\n\t\t)\n\t\treturn nil\n\t}\n\n\t// Handle doesn't already exist, try to reattach\n\tvar taskState TaskState\n\tif err := handle.GetDriverState(&taskState); err != nil {\n\t\td.logger.Error(\"failed to decode task state from handle\", \"error\", err, \"task_id\", handle.Config.ID)\n\t\treturn fmt.Errorf(\"failed to decode task state from handle: %v\", err)\n\t}\n","sourceCodeStart":387,"sourceCodeEnd":423,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/drivers/exec/driver.go#L387-L423","documentation":"Defensive guard in exec Driver.RecoverTask: a nil *drivers.TaskHandle was passed, which is a programming error by the plugin caller (a real recovery always has a handle); no handle means no task ID to look up.","triggerScenarios":"Calling Driver.RecoverTask(nil), typically from host-side task restoration code paths where a handle failed to deserialize or was not produced by StartTask.","commonSituations":"Plugin/host integration bugs; custom code paths or tests invoking RecoverTask without a handle; restore logic after agent restart with corrupted handle data.","solutions":["Ensure RecoverTask is only called with a valid handle from drivers.TaskRegistry or StartTask output","Guard the call site with a nil check before invoking","Fix handle construction/serialization upstream so nil handles aren't propagated"],"exampleFix":"// before\nif err := driver.RecoverTask(nil); err != nil { ... }\n// after\nif handle == nil {\n  return fmt.Errorf(\"no handle to recover\")\n}\nif err := driver.RecoverTask(handle); err != nil { ... }","handlingStrategy":"type-guard","validationCode":"if handle == nil {\n  return errors.New(\"refusing to recover: nil handle\")\n}","typeGuard":"func hasHandle(h *drivers.TaskHandle) bool {\n  return h != nil && h.Config != nil && h.Config.ID != \"\"\n}","tryCatchPattern":"if !hasHandle(handle) {\n  return fmt.Errorf(\"skip recovery: no valid handle\")\n}\nif err := driver.RecoverTask(handle); err != nil {\n  return fmt.Errorf(\"recover failed: %w\", err)\n}","preventionTips":["Only pass handles obtained from the task registry or StartTask","Never persist/replay raw handle pointers across process boundaries","Add nil assertions in restore code paths"],"tags":["nomad","exec-driver","task-lifecycle","api-misuse"],"backgroundTag":"nil-handle-argument","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"}