{"record":{"id":"fb2060ce6741fa02","repo":"apache/hadoop","slug":"not-submitting-job-job-directory-already-exist","errorCode":null,"errorMessage":"Not submitting job. Job directory {} already exists!! This is unexpected.Please check what's there in that directory","messagePattern":"Not submitting job\\. Job directory (.+?) already exists!! This is unexpected\\.Please check what's there in that directory","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/JobResourceUploader.java","lineNumber":165,"sourceCode":"\n    if (!(conf.getBoolean(Job.USED_GENERIC_PARSER, false))) {\n      LOG.warn(\"Hadoop command-line option parsing not performed. \"\n          + \"Implement the Tool interface and execute your application \"\n          + \"with ToolRunner to remedy this.\");\n    }\n\n    //\n    // Figure out what fs the JobTracker is using. Copy the\n    // job to it, under a temporary name. This allows DFS to work,\n    // and under the local fs also provides UNIX-like object loading\n    // semantics. (that is, if the job file is deleted right after\n    // submission, we can still run the submission to completion)\n    //\n\n    // Create a number of filenames in the JobTracker's fs namespace\n    LOG.debug(\"default FileSystem: \" + jtFs.getUri());\n    if (jtFs.exists(submitJobDir)) {\n      throw new IOException(\"Not submitting job. Job directory \" + submitJobDir\n          + \" already exists!! This is unexpected.Please check what's there in\"\n          + \" that directory\");\n    }\n    // Create the submission directory for the MapReduce job.\n    submitJobDir = jtFs.makeQualified(submitJobDir);\n    submitJobDir = new Path(submitJobDir.toUri().getPath());\n    FsPermission mapredSysPerms =\n        new FsPermission(JobSubmissionFiles.JOB_DIR_PERMISSION);\n    mkdirs(jtFs, submitJobDir, mapredSysPerms);\n\n    if (!conf.getBoolean(MRJobConfig.MR_AM_STAGING_DIR_ERASURECODING_ENABLED,\n        MRJobConfig.DEFAULT_MR_AM_STAGING_ERASURECODING_ENABLED)) {\n      disableErasureCodingForPath(submitJobDir);\n    }\n\n    // Get the resources that have been added via command line arguments in the\n    // GenericOptionsParser (i.e. files, libjars, archives).\n    Collection<String> files = conf.getStringCollection(\"tmpfiles\");","sourceCodeStart":147,"sourceCodeEnd":183,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapreduce/JobResourceUploader.java#L147-L183","documentation":"JobResourceUploader.uploadResourcesInternal creates the per-job staging directory (<yarn.app.mapreduce.am.staging-dir>/<user>/.staging/job_<...>) and, being the first writer, first checks jtFs.exists(submitJobDir). A brand-new submission always receives a unique JobID, so an existing directory means the same job id is being submitted again; the code refuses with IOException('Not submitting job. Job directory ... already exists!!') to avoid clobbering the earlier run's artifacts.","triggerScenarios":"Calling job.submit() twice on the same Job object (the JobID was already allocated on the first call); a prior submission with the same id died after mkdir; a Configuration that pins mapreduce.job.id so two runs collide; two concurrent submitters reusing one fixed job id.","commonSituations":"Catch-and-resubmit retry loops that reuse the Job instance; staging leftovers under /tmp/hadoop-yarn/staging/<user>/.staging/ from a killed OOM'd submission; staging dirs shared between environments because yarn.app.mapreduce.am.staging-dir is misconfigured; scripts that copy a job.xml containing a literal mapreduce.job.id.","solutions":["Never resubmit a submitted Job — allocate a new Job.getInstance(conf) for every attempt so a fresh JobID is minted","Delete the stale directory and retry: hdfs dfs -rm -r /tmp/hadoop-yarn/staging/<user>/.staging/job_XXXX then resubmit","Search your conf for a hardcoded mapreduce.job.id and remove it","If it recurs systematically, verify two processes are not submitting with the same id and that the staging dir is not shared across clusters"],"exampleFix":"// before\njob.submit();\n// later, after a transient failure:\njob.submit(); // same JobID -> IOException: Job directory already exists!!\n\n// after\njob.submit();\n// rerun path:\njob = Job.getInstance(conf); // new JobID, new staging dir\njob.submit();","handlingStrategy":"validation","validationCode":"// reject accidental double-submit of one Job instance\nif (submitted) {\n  throw new IllegalStateException(\"This Job was already submitted; create a new Job.getInstance(conf)\");\n}\n// optional: verify no stale dir for a pinned id before submitting\nif (conf.get(MRJobConfig.JOB_ID) != null) {\n  Path staging = new Path(conf.get(\"yarn.app.mapreduce.am.staging-dir\"),\n      Path.curPath... /* user */ + \"/.staging/\" + conf.get(MRJobConfig.JOB_ID));\n  if (fs.exists(staging)) { /* refuse or clean */ }\n}","typeGuard":null,"tryCatchPattern":"try {\n  job.submit();\n} catch (IOException e) {\n  if (e.getMessage() != null && e.getMessage().contains(\"already exists!!\")) {\n    throw new IOException(\"Staging dir for this JobID exists; delete it or submit a fresh Job\", e);\n  }\n  throw e;\n}","preventionTips":["One Job instance, one submit() call — encode this in your job-runner base class","Never copy mapreduce.job.id between submissions","Clean .staging leftovers only when no active submission is running"],"tags":["staging-directory","job-submission","duplicate-submission","yarn","mapreduce"],"backgroundTag":"path-already-exists","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}