spring-projects/spring-ai · error · IOException
Invalid filename for file '': must be a single path segment
Error message
Invalid filename for file '': must be a single path segment ''
What it means
AnthropicSkillsResponseHelper.resolveSafeChildPath validates filenames returned by the Anthropic Files/Skills API before writing them under a target directory. It throws this IOException when the decoded filename parses to a path with more than one segment (e.g. 'a/b' or a multi-component name), because each downloaded file must map to exactly one entry inside the target dir. It is a path-traversal hardening guard.
Source
Thrown at models/spring-ai-anthropic/src/main/java/org/springframework/ai/anthropic/AnthropicSkillsResponseHelper.java:164
* 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 + "'");
}
return resolved;
}
View on GitHub (pinned to 98a7beda4f)
Solutions
- Inspect the file's name field returned by the API and use only its final segment before requesting/downloading.
- If nested layout is expected, strip the directory portion yourself (Paths.get(raw).getFileName()) and create parent dirs explicitly rather than passing a multi-segment name.
- Check whether your skill package metadata is emitting paths instead of plain filenames and fix the packager.
- Catch IOException from resolveSafeChildPath and skip/log the offending file instead of failing the whole download.
Example fix
// before Path resolved = helper.filePath(fileId, "output/sub/report.md"); // after String raw = "output/sub/report.md"; String onlyName = Paths.get(raw).getFileName().toString(); Path resolved = helper.filePath(fileId, onlyName);
Defensive patterns
Strategy: validation
Validate before calling
static boolean isSafeFileName(String name) {
return name != null && name.matches("[A-Za-z0-9._-]+")
&& !name.equals(".") && !name.equals("..");
} Type guard
static String safeFileName(String raw) {
if (raw == null || !isSafeFileName(raw)) {
throw new IllegalArgumentException("Unsafe filename: " + raw);
}
return raw;
} Try / catch
try {
Path p = helper.filePath(fileId, name);
} catch (IOException e) {
log.warn("Skipping unsafe filename {}: {}", name, e.getMessage());
} Prevention
- Always take only the final path segment of any API-supplied filename.
- Whitelist filename characters instead of blacklisting separators.
- Never pass user- or API-controlled paths directly to file-writing helpers.
When it happens
Trigger: Anthropic API returns a file whose name field contains a path separator (e.g. 'sub/file.txt' or a Windows-style 'sub\file.txt') when resolving a skill file via the filePath helper, so resolveSafeChildPath sees getNameCount() != 1.
Common situations: Skill bundles that internally reference files in subdirectories; a provider-side change in how the filename header is emitted; malicious or buggy filename coming back from the API; platform path quirks where the raw name contains separators.
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
- Invalid filename for file '': ''
- Invalid filename for file '': resolves outside target direct
- Request failed
- Could not read content length
- Failed to write request body
AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11).
Data as JSON: /api/errors/28d7621fba18c786.
Report an issue: GitHub.