{"record":{"id":"5988a61f121eb58f","repo":"hashicorp/nomad","slug":"handle-cannot-be-nil-5988a6","errorCode":null,"errorMessage":"handle cannot be nil","messagePattern":"handle cannot be nil","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"drivers/java/driver.go","lineNumber":380,"sourceCode":"\tif err != nil {\n\t\t// return no error, as it isn't an error to not find java, it just means we\n\t\t// can't use it.\n\t\tfp.Health = drivers.HealthStateUndetected\n\t\tfp.HealthDescription = \"\"\n\t\treturn fp\n\t}\n\n\tfp.Attributes[driverAttr] = pstructs.NewBoolAttribute(true)\n\tfp.Attributes[driverVersionAttr] = pstructs.NewStringAttribute(version)\n\tfp.Attributes[\"driver.java.runtime\"] = pstructs.NewStringAttribute(jdkJRE)\n\tfp.Attributes[\"driver.java.vm\"] = pstructs.NewStringAttribute(vm)\n\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.Debug(\"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":362,"sourceCodeEnd":398,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/drivers/java/driver.go#L362-L398","documentation":"This error is returned by the Java driver's RecoverTask method when called with a nil *drivers.TaskHandle. RecoverTask is used after a Nomad client/agent restart to reattach to a running task's executor; without a valid handle there is nothing to recover. It is a defensive guard against programming errors in the driver manager.","triggerScenarios":"The Nomad client's driver manager calls d.RecoverTask(nil), typically due to a bug in handle restoration from the client state store or a nil handle passed during plugin recovery.","commonSituations":"Post-restore bugs where the handle was not persisted or failed to deserialize to a non-nil value; custom code or plugins invoking the driver API directly with a nil handle.","solutions":["Ensure the driver manager restores and passes a valid TaskHandle from client state","Fix the caller so nil handles are skipped or re-created before RecoverTask","If writing custom tooling, check the handle for nil before calling RecoverTask","Restart the Nomad client to re-run recovery with intact state; report a bug if it persists"],"exampleFix":"// before\nerr := driver.RecoverTask(nil)\n// after\nif handle != nil {\n    err = driver.RecoverTask(handle)\n}","handlingStrategy":"type-guard","validationCode":"if handle == nil {\n    return errors.New(\"cannot recover: task handle is nil\")\n}","typeGuard":"func validHandle(h *drivers.TaskHandle) bool { return h != nil && h.Config.ID != \"\" }","tryCatchPattern":"if err := driver.RecoverTask(handle); err != nil {\n    if strings.Contains(err.Error(), \"handle cannot be nil\") {\n        // skip or recreate handle before retrying\n    }\n}","preventionTips":["Never call driver APIs directly with nil handles; let the driver manager manage recovery","Persist and reload handles from client state correctly","Add unit tests for recovery paths with nil/empty handles"],"tags":["nomad","java-driver","recover-task","nil-check"],"backgroundTag":"nil-handle","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"}