apache/hadoop · error · IllegalArgumentException

Resource name must be relative

Error message

Resource name must be relative

What it means

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.

Source

Thrown at hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-common/src/main/java/org/apache/hadoop/mapreduce/v2/util/LocalResourceBuilder.java:128

        if (p.getName().equals(DistributedCache.WILDCARD)) {
          p = p.getParent();
          linkName = p.getName() + Path.SEPARATOR + DistributedCache.WILDCARD;
        }

        p = remoteFS.resolvePath(p.makeQualified(remoteFS.getUri(),
            remoteFS.getWorkingDirectory()));

        // If there's no wildcard, try using the fragment for the link
        if (linkName == null) {
          linkName = u.getFragment();

          // Because we don't know what's in the fragment, we have to handle
          // it with care.
          if (linkName != null) {
            Path linkPath = new Path(linkName);

            if (linkPath.isAbsolute()) {
              throw new IllegalArgumentException("Resource name must be "
                  + "relative");
            }

            linkName = linkPath.toUri().getPath();
          }
        } else if (u.getFragment() != null) {
          throw new IllegalArgumentException("Invalid path URI: " + p +
              " - cannot contain both a URI fragment and a wildcard");
        }

        // If there's no wildcard or fragment, just link to the file name
        if (linkName == null) {
          linkName = p.getName();
        }

        LocalResource orig = localResources.get(linkName);
        if(orig != null && !orig.getResource().equals(URL.fromURI(p.toUri()))) {
          LOG.warn(getResourceDescription(orig.getType()) + orig.getResource()

View on GitHub (pinned to 2add963021)

Solutions

  1. Use a plain relative name as the fragment: '#lib.so' — the file localizes into the task working dir under that name
  2. Drop the fragment entirely to let the link name default to the file name (p.getName())
  3. If a specific absolute path is required, symlink it from the task's working directory after localization

Example fix

// before
job.addCacheFile(new URI("hdfs://nn/native/libhadoop.so#/usr/lib/libhadoop.so")); // throws

// after
job.addCacheFile(new URI("hdfs://nn/native/libhadoop.so#libhadoop.so"));
// then reference it as ./libhadoop.so from the task working directory
Defensive patterns

Strategy: validation

Validate before calling

static String relativeFragmentOrNull(URI u) {
  String f = u.getFragment();
  if (f == null) return null;
  Path link = new Path(f);
  if (link.isAbsolute()) {
    throw new IllegalArgumentException("Fragment must be relative: " + u);
  }
  return f;
}
// call before job.addCacheFile(u)

Try / catch

try {
  job.addCacheFile(new URI(rawUri));
} catch (IllegalArgumentException e) {
  throw new IllegalArgumentException("Bad cache URI '" + rawUri
      + "': use a relative #fragment, e.g. file.jar#file.jar", e);
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of apache/hadoop@2add963021 (2026-08-22). Data as JSON: /api/errors/d79179d59ad5749d. Report an issue: GitHub.