{"record":{"id":"768954d620a43c6f","repo":"hashicorp/nomad","slug":"error-handle-cannot-be-nil","errorCode":null,"errorMessage":"error: handle cannot be nil","messagePattern":"error: handle cannot be nil","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"drivers/qemu/driver.go","lineNumber":288,"sourceCode":"\n\tmatches := versionRegex.FindStringSubmatch(out)\n\tif len(matches) != 2 {\n\t\tfingerprint.Health = drivers.HealthStateUndetected\n\t\tfingerprint.HealthDescription = fmt.Sprintf(\"Failed to parse qemu version from %v\", out)\n\t\treturn fingerprint\n\t}\n\tcurrentQemuVersion := matches[1]\n\n\tfingerprint.Attributes[driverAttr] = pstructs.NewBoolAttribute(true)\n\tfingerprint.Attributes[driverVersionAttr] = pstructs.NewStringAttribute(currentQemuVersion)\n\tfingerprint.Attributes[driverEmulatorsAttr] = pstructs.NewStringAttribute(strings.Join(emulators, \",\"))\n\n\treturn fingerprint\n}\n\nfunc (d *Driver) RecoverTask(handle *drivers.TaskHandle) error {\n\tif handle == nil {\n\t\treturn fmt.Errorf(\"error: 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\tvar taskState TaskState\n\tif err := handle.GetDriverState(&taskState); err != nil {\n\t\td.logger.Error(\"failed to decode taskConfig state from handle\", \"error\", err, \"task_id\", handle.Config.ID)\n\t\treturn fmt.Errorf(\"failed to decode taskConfig state from handle: %v\", err)\n\t}\n\n\tplugRC, err := pstructs.ReattachConfigToGoPlugin(taskState.ReattachConfig)","sourceCodeStart":270,"sourceCodeEnd":306,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/drivers/qemu/driver.go#L270-L306","documentation":"Returned by the QEMU driver's RecoverTask when called with a nil task handle; recovery cannot restore state for a missing/stale handle, indicating the task ID is unknown after a restart.","triggerScenarios":"Calling Driver.RecoverTask(nil), e.g. when a caller's handle lookup failed but nil was passed through instead of being checked.","commonSituations":"Client plugin re-attachment logic passing an unpopulated handle after a failed GetTaskHandle; tests invoking RecoverTask without constructing a handle.","solutions":["Ensure the caller obtains a valid *drivers.TaskHandle (via drivers.NewTaskHandle + Config) before calling RecoverTask.","Add a nil check at the call site and skip/log instead of calling RecoverTask with nil.","If handles come from persisted state, verify that state was successfully loaded before recovery."],"exampleFix":"// before\nerr := d.RecoverTask(h)\n// after\nif h == nil {\n    return errors.New(\"no task handle to recover\")\n}\nerr := d.RecoverTask(h)","handlingStrategy":"type-guard","validationCode":"if handle == nil {\n    return errors.New(\"refusing to recover: task handle is nil\")\n}","typeGuard":"func recoverable(h *drivers.TaskHandle) bool { return h != nil && h.Config != nil && h.Config.ID != \"\" }","tryCatchPattern":"if !recoverable(h) {\n    return errors.New(\"no valid handle to recover\")\n}\nerr := d.RecoverTask(h)","preventionTips":["Never pass a nil handle into RecoverTask; obtain it from loaded client state.","Assert handles are populated (Config.ID set) before recovery attempts."],"tags":["nomad","qemu-driver","nil-handle","recover-task"],"backgroundTag":"nil-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"}