spring-projects/spring-ai · error · IOException
Invalid filename for file '': absolute path ''
Error message
Invalid filename for file '': absolute path ''
What it means
resolveSafeChildPath rejects filenames that are absolute paths or carry a filesystem root. Since names originate from model-influenced API metadata, allowing '/etc/passwd' or 'C:\evil' would enable writing outside the target directory. The check name.isAbsolute() || name.getRoot() != null throws an IOException naming the offending raw value.
Source
Thrown at models/spring-ai-anthropic/src/main/java/org/springframework/ai/anthropic/AnthropicSkillsResponseHelper.java:161
* Validate an API-provided filename and resolve it to a child of {@code targetDir}.
* Rejects null/blank names, absolute paths, names containing path separators or
* {@code .}/{@code ..} segments, and names that resolve outside {@code targetDir}.
* Filenames come from model-influenced API metadata and must not be trusted as safe
* path components.
*/
static Path resolveSafeChildPath(Path targetDir, @Nullable String rawName, String fileId) throws IOException {
if (rawName == null || rawName.isBlank()) {
throw new IOException("Invalid filename for file '" + fileId + "': null or blank");
}
Path name;
try {
name = Path.of(rawName);
}
catch (InvalidPathException ex) {
throw new IOException("Invalid filename for file '" + fileId + "': " + rawName, ex);
}
if (name.isAbsolute() || name.getRoot() != null) {
throw new IOException("Invalid filename for file '" + fileId + "': absolute path '" + rawName + "'");
}
if (name.getNameCount() != 1) {
throw new IOException(
"Invalid filename for file '" + fileId + "': must be a single path segment '" + rawName + "'");
}
String only = name.getName(0).toString();
if (only.equals(".") || only.equals("..")) {
throw new IOException("Invalid filename for file '" + fileId + "': '" + rawName + "'");
}
// One extra hardening check to make sure nothing fell through the cracks above
// (future tweaks to the rules, odd platform path quirks, etc.).
Path base = targetDir.toAbsolutePath().normalize();
Path resolved = base.resolve(only).normalize();
if (!resolved.startsWith(base)) {
throw new IOException(
"Invalid filename for file '" + fileId + "': resolves outside target directory '" + rawName + "'");
}View on GitHub (pinned to 98a7beda4f)
Solutions
- Strip any directory portion and keep only the base filename (e.g. Path.of(rawName).getFileName()) before resolving, after verifying it is a single safe segment.
- Reject/skip the file entry and log it — absolute paths from this API are never legitimate.
- Treat the occurrence as a data-quality or security signal: inspect the upstream response source for prompt injection.
- Keep using resolveSafeChildPath for every API-derived filename; do not bypass it with direct Path.of/Paths.get(targetDir, rawName).
Example fix
// before
Path p = resolveSafeChildPath(targetDir, "/etc/passwd", fileId); // throws
// after
String bare = Path.of(rawName).getFileName().toString();
if (!bare.equals(rawName)) {
log.warn("Rejected absolute path from API for file {}", fileId);
return null;
}
Path p = resolveSafeChildPath(targetDir, bare, fileId); Defensive patterns
Strategy: validation
Validate before calling
Path n = Path.of(rawName);
if (n.isAbsolute() || n.getRoot() != null || n.getNameCount() != 1) {
throw new SecurityException("Refusing non-simple filename from API: " + rawName);
} Type guard
static boolean isSimpleRelativeName(String rawName) {
try {
Path n = Path.of(rawName);
return !n.isAbsolute() && n.getRoot() == null && n.getNameCount() == 1;
} catch (InvalidPathException e) { return false; }
} Try / catch
try {
Path p = AnthropicSkillsResponseHelper.resolveSafeChildPath(targetDir, rawName, fileId);
} catch (IOException e) {
if (e.getMessage() != null && e.getMessage().contains("absolute path")) {
securityLog.warn("Path-traversal attempt in file {}: {}", fileId, rawName);
} else throw e;
} Prevention
- Never bypass resolveSafeChildPath with direct targetDir.resolve(rawName).
- Log and alert on absolute-path filenames — they can indicate prompt injection.
- Keep the helper's validation as the single choke point for API-derived filenames.
When it happens
Trigger: A file entry whose name field contains an absolute path like '/tmp/x.txt', '\\server\share\f' (UNC), or a Windows rooted path 'C:\file.txt' passed to resolveSafeChildPath.
Common situations: A compromised or hallucinating model returning full paths instead of bare filenames; proxying responses from another system that emits absolute paths; hostile API payloads probing for path traversal (this check is part of the traversal defense).
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
- Maximum of 8 skills per request. Provided:
- Invalid filename for file '': null or blank
- Invalid filename for file '':
- Unsupported media type: . Supported types are: images (image
- Unsupported media data type: . Expected byte[] or String.
AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11).
Data as JSON: /api/errors/a8352b05cf39865b.
Report an issue: GitHub.