{"record":{"id":"01c52cfe8923f9f7","repo":"apache/dolphinscheduler","slug":"jobrunid-is-empty-cannot-track-application-status","errorCode":null,"errorMessage":"jobRunId is empty, cannot track application status","messagePattern":"jobRunId is empty, cannot track application status","errorType":"exception","errorClass":"EmrServerlessTaskException","httpStatus":null,"severity":"error","filePath":"dolphinscheduler-task-plugin/dolphinscheduler-task-emr-serverless/src/main/java/org/apache/dolphinscheduler/plugin/task/emrserverless/EmrServerlessTask.java","lineNumber":157,"sourceCode":"            log.info(\"Successfully submitted EMR Serverless job run, jobRunId: {}\", jobRunId);\n\n        } catch (EmrServerlessTaskException | SdkBaseException e) {\n            log.error(\"EMR Serverless task submit failed\", e);\n            throw new TaskException(\"EMR Serverless task submit failed\", e);\n        }\n    }\n\n    @Override\n    public void trackApplicationStatus() throws TaskException {\n        try {\n            // Recover jobRunId from appIds if needed (failover case)\n            if (StringUtils.isEmpty(jobRunId) && StringUtils.isNotEmpty(getAppIds())) {\n                jobRunId = getAppIds();\n                log.info(\"Recovered EMR Serverless jobRunId from appIds: {}\", jobRunId);\n            }\n\n            if (StringUtils.isEmpty(jobRunId)) {\n                throw new EmrServerlessTaskException(\"jobRunId is empty, cannot track application status\");\n            }\n\n            String currentState = getJobRunState();\n            while (WAITING_STATES.contains(currentState)) {\n                TimeUnit.SECONDS.sleep(10);\n                currentState = getJobRunState();\n            }\n\n            final int exitCode = mapStateToExitCode(currentState);\n            setExitStatusCode(exitCode);\n            log.info(\"EMR Serverless job run [{}] finished with state: {}, exitCode: {}\",\n                    jobRunId, currentState, exitCode);\n\n        } catch (EmrServerlessTaskException | SdkBaseException e) {\n            log.error(\"EMR Serverless task tracking failed\", e);\n            setExitStatusCode(TaskConstants.EXIT_CODE_FAILURE);\n        } catch (InterruptedException e) {\n            Thread.currentThread().interrupt();","sourceCodeStart":139,"sourceCodeEnd":175,"githubUrl":"https://github.com/apache/dolphinscheduler/blob/02eac45a1b6676e639fcbfb4be2243de5771b05d/dolphinscheduler-task-plugin/dolphinscheduler-task-emr-serverless/src/main/java/org/apache/dolphinscheduler/plugin/task/emrserverless/EmrServerlessTask.java#L139-L175","documentation":"trackApplicationStatus requires a jobRunId to poll GetJobRun. It first tries the in-memory field, then falls back to appIds persisted for failover recovery. If both are empty, it throws EmrServerlessTaskException with this message, because the plugin cannot identify which job run to monitor.","triggerScenarios":"trackApplicationStatus is invoked without submitApplication having succeeded in this process and getAppIds() returns null/empty — typically after a worker failover where appIds was never persisted (submit crashed before setAppIds), or the task was defined so submitApplication is skipped.","commonSituations":"Worker crashed between StartJobRun success and setAppIds persistence; task instance retried on a different worker without stored appIds; DB row for appIds wiped or task configured to run track-only in custom code.","solutions":["Ensure submitApplication completed and setAppIds(jobRunId) persisted appIds before tracking (check for earlier 'EMR Serverless task submit failed' errors)","Re-run the task instance so a fresh jobRunId is submitted and stored","If recovering manually, find the jobRunId in the AWS console/CloudTrail and set it as the task's appIds in the DB before retry","Check the worker logs of the failed-over instance for the originally submitted jobRunId"],"exampleFix":"// before (worker died between submit and persist)\njobRunId = result.getJobRunId(); // process killed here -> appIds lost\n// after\njobRunId = result.getJobRunId();\nsetAppIds(jobRunId); // must run immediately after submit; add log/state persist so failover can recover","handlingStrategy":"validation","validationCode":"// before relying on tracking/failover recovery\nString jobRunId = getAppIds();\nif (StringUtils.isBlank(jobRunId)) {\n    throw new IllegalStateException(\"appIds not persisted; cannot recover EMR Serverless jobRunId\");\n}","typeGuard":"boolean hasTrackableJob(EmrServerlessTask t) {\n    return StringUtils.isNotEmpty(t.getAppIds());\n}","tryCatchPattern":"try {\n    task.trackApplicationStatus();\n} catch (EmrServerlessTaskException e) {\n    if (e.getMessage().contains(\"jobRunId is empty\")) {\n        log.error(\"No jobRunId persisted for failover; resubmit the task instance\", e);\n    }\n    throw e;\n}","preventionTips":["Always persist appIds immediately after StartJobRun (setAppIds is the failover source)","Never skip submitApplication when tracking is expected in the same lifecycle","Monitor for earlier submit failures before investigating tracking errors","Test failover recovery by restarting a worker mid-run in staging"],"tags":["aws","emr-serverless","state-recovery","failover"],"backgroundTag":"empty-required-field","analyzedSha":"02eac45a1b6676e639fcbfb4be2243de5771b05d","analyzedAt":"2026-09-06T17:43:00.555Z","contentChangedAt":"2026-09-06T17:43:00.555Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}