spring-projects/spring-ai · error · IllegalArgumentException

Resource URI resolves outside the cache directory:

Error message

Resource URI resolves outside the cache directory: 

What it means

getCachedFile computes the target cache file from a UUID of the resource URI and then performs a canonical-path containment check: the resolved file must live inside the cache directory. If the canonical path escapes the cache root (symlink trickery or crafted resource names), it throws IllegalArgumentException to block a path-traversal style escape.

Source

Thrown at models/spring-ai-transformers/src/main/java/org/springframework/ai/transformers/ResourceCacheService.java:140

				FileCopyUtils.copy(StreamUtils.copyToByteArray(originalResource.getInputStream()), cachedFile);
				logger.info("Caching the " + originalResource.toString() + " resource to: " + cachedFile);
			}
			return new FileUrlResource(cachedFile.getAbsolutePath());
		}
		catch (Exception e) {
			throw new IllegalStateException("Failed to cache the resource: " + originalResource.getDescription(), e);
		}
	}

	private File getCachedFile(Resource originalResource) throws IOException {
		var resourceParentFolder = new File(this.cacheDirectory,
				UUID.nameUUIDFromBytes(pathWithoutLastSegment(originalResource.getURI())).toString());
		resourceParentFolder.mkdirs();
		String newFileName = getCacheName(originalResource);
		File cachedFile = new File(resourceParentFolder, newFileName);
		String canonicalCache = this.cacheDirectory.getCanonicalPath() + File.separator;
		if (!cachedFile.getCanonicalPath().startsWith(canonicalCache)) {
			throw new IllegalArgumentException(
					"Resource URI resolves outside the cache directory: " + originalResource.getDescription());
		}
		return cachedFile;
	}

	private byte[] pathWithoutLastSegment(URI uri) {
		String path = uri.toASCIIString();
		var pathBeforeLastSegment = path.substring(0, path.lastIndexOf('/') + 1);
		return pathBeforeLastSegment.getBytes();
	}

	private String getCacheName(Resource originalResource) throws IOException {
		String fileName = originalResource.getFilename();
		Assert.hasText(fileName, "The file name must should not be null or empty");
		String fragment = originalResource.getURI().getFragment();
		return !StringUtils.hasText(fragment) ? fileName : fileName + "_" + fragment;
	}

View on GitHub (pinned to 98a7beda4f)

Solutions

  1. Use plain, well-formed model resource URIs that resolve within the cache directory; remove ../ segments and symlinks.
  2. Point the cache directory at the real (canonical) location, not a symlinked path.
  3. If resources legitimately live elsewhere, copy them into the cache directory first or configure the cache dir accordingly.

Example fix

// before
new UrlResource("file:/models/../../etc/model.onnx") // escapes cache dir
// after
new UrlResource("https://huggingface.co/model/onnx/model.onnx") // normal remote resource
Defensive patterns

Strategy: validation

Validate before calling

File cacheDir = new File("/var/cache/spring-ai");
File target = new File(resource.getURI().getPath());
if (!target.getCanonicalPath().startsWith(cacheDir.getCanonicalPath() + File.separator)) {
    throw new IllegalArgumentException("Refusing resource outside cache dir");
}

Prevention

When it happens

Trigger: Calling getCachedResource (via cachedFile) with a Resource whose URI path, after canonicalization, resolves outside the configured cache directory — e.g. URIs containing ../ segments or pointing through symlinks out of the cache root.

Common situations: Passing user-supplied URLs/paths as model resources; symlinks inside the cache directory pointing elsewhere; resources on different mount points that canonicalize outside the cache root.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11). Data as JSON: /api/errors/33cbc3f0bae66e5b. Report an issue: GitHub.