{"record":{"id":"d79179d59ad5749d","repo":"apache/hadoop","slug":"resource-name-must-be-relative","errorCode":null,"errorMessage":"Resource name must be relative","messagePattern":"Resource name must be relative","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-common/src/main/java/org/apache/hadoop/mapreduce/v2/util/LocalResourceBuilder.java","lineNumber":128,"sourceCode":"        if (p.getName().equals(DistributedCache.WILDCARD)) {\n          p = p.getParent();\n          linkName = p.getName() + Path.SEPARATOR + DistributedCache.WILDCARD;\n        }\n\n        p = remoteFS.resolvePath(p.makeQualified(remoteFS.getUri(),\n            remoteFS.getWorkingDirectory()));\n\n        // If there's no wildcard, try using the fragment for the link\n        if (linkName == null) {\n          linkName = u.getFragment();\n\n          // Because we don't know what's in the fragment, we have to handle\n          // it with care.\n          if (linkName != null) {\n            Path linkPath = new Path(linkName);\n\n            if (linkPath.isAbsolute()) {\n              throw new IllegalArgumentException(\"Resource name must be \"\n                  + \"relative\");\n            }\n\n            linkName = linkPath.toUri().getPath();\n          }\n        } else if (u.getFragment() != null) {\n          throw new IllegalArgumentException(\"Invalid path URI: \" + p +\n              \" - cannot contain both a URI fragment and a wildcard\");\n        }\n\n        // If there's no wildcard or fragment, just link to the file name\n        if (linkName == null) {\n          linkName = p.getName();\n        }\n\n        LocalResource orig = localResources.get(linkName);\n        if(orig != null && !orig.getResource().equals(URL.fromURI(p.toUri()))) {\n          LOG.warn(getResourceDescription(orig.getType()) + orig.getResource()","sourceCodeStart":110,"sourceCodeEnd":146,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-common/src/main/java/org/apache/hadoop/mapreduce/v2/util/LocalResourceBuilder.java#L110-L146","documentation":"In LocalResourceBuilder.createLocalResources, when a cache URI has no wildcard the URI fragment (the part after '#') is used as the symlink name for the localized file. Because the fragment is attacker/untrusted-shaped input, the builder validates it: if new Path(linkName).isAbsolute() it throws IllegalArgumentException('Resource name must be relative'). Absolute symlink targets inside the sandboxed working directory are meaningless and unsafe, so they are rejected up front.","triggerScenarios":"A distributed-cache entry whose fragment begins with '/' (or is otherwise an absolute path), e.g. 'hdfs://nn/lib/lib.so#/tmp/lib.so' passed to job.addCacheFile or mapreduce.job.cache.files.","commonSituations":"Users trying to control where the file appears by putting an absolute path after '#'; porting shell scripts that reference fixed paths; fragments accidentally containing a leading slash from string concatenation.","solutions":["Use a plain relative name as the fragment: '#lib.so' — the file localizes into the task working dir under that name","Drop the fragment entirely to let the link name default to the file name (p.getName())","If a specific absolute path is required, symlink it from the task's working directory after localization"],"exampleFix":"// before\njob.addCacheFile(new URI(\"hdfs://nn/native/libhadoop.so#/usr/lib/libhadoop.so\")); // throws\n\n// after\njob.addCacheFile(new URI(\"hdfs://nn/native/libhadoop.so#libhadoop.so\"));\n// then reference it as ./libhadoop.so from the task working directory","handlingStrategy":"validation","validationCode":"static String relativeFragmentOrNull(URI u) {\n  String f = u.getFragment();\n  if (f == null) return null;\n  Path link = new Path(f);\n  if (link.isAbsolute()) {\n    throw new IllegalArgumentException(\"Fragment must be relative: \" + u);\n  }\n  return f;\n}\n// call before job.addCacheFile(u)","typeGuard":null,"tryCatchPattern":"try {\n  job.addCacheFile(new URI(rawUri));\n} catch (IllegalArgumentException e) {\n  throw new IllegalArgumentException(\"Bad cache URI '\" + rawUri\n      + \"': use a relative #fragment, e.g. file.jar#file.jar\", e);\n}","preventionTips":["Treat the URI fragment as a file name, never a path — no leading '/'","Reference localized files via ./name relative to the task working directory"],"tags":["hadoop","mapreduce","distributed-cache","symlink","uri","fragment"],"backgroundTag":"invalid-distributed-cache-uri","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}