alibaba/nacos · warning · FileAlreadyExistsException
Skill directory already exists: {skillDir}
Error message
Skill directory already exists: {skillDir} What it means
Thrown by syncToLocalCore as a java.nio.file.FileAlreadyExistsException when the ExistingDirectoryStrategy is FAIL and the target skill directory already exists on disk. This is an intentional 'do not clobber' guard: the caller explicitly chose FAIL and the directory was present.
Source
Thrown at api/src/main/java/com/alibaba/nacos/api/ai/model/skills/SkillUtils.java:389
}
/**
* Core implementation for syncing Skill to local directory.
* This method contains the common logic for all syncToLocal variants.
*
* @param skill the Skill object to sync
* @param skillDir the target skill directory path
* @param basePath the base directory path
* @param strategy the strategy for handling existing directories
* @throws IOException if file operations fail
* @throws FileAlreadyExistsException if directory exists and strategy is FAIL
*/
private static void syncToLocalCore(Skill skill, Path skillDir, Path basePath,
ExistingDirectoryStrategy strategy) throws IOException {
// Step 1: If strategy is FAIL, check if directory exists and throw exception immediately
if (strategy == ExistingDirectoryStrategy.FAIL) {
if (Files.exists(skillDir) && Files.isDirectory(skillDir)) {
throw new FileAlreadyExistsException("Skill directory already exists: " + skillDir);
}
}
// Step 2: Create temporary directory and write all files
String dirName = skillDir.getFileName().toString();
Path tempSkillDir = basePath.resolve(dirName + ".tmp." + System.currentTimeMillis());
try {
// Create temporary skill directory
Files.createDirectories(tempSkillDir);
// Write SKILL.md file
String markdownContent = toMarkdown(skill);
Path skillMdPath = tempSkillDir.resolve("SKILL.md");
Files.write(skillMdPath, markdownContent.getBytes(StandardCharsets.UTF_8));
// Write resource files
if (skill.getResource() != null && !skill.getResource().isEmpty()) {View on GitHub (pinned to 9b989acdf1)
Solutions
- Switch the strategy to OVERWRITE (delete and recreate) or BACKUP (rename old with timestamp) if clobbering is acceptable.
- If FAIL was intentional, catch FileAlreadyExistsException and treat it as 'already synced — skip'.
- Check Files.exists(skillDir) first and branch on whether to sync at all.
Example fix
// before
SkillUtils.syncToLocal(skill, baseDir, ExistingDirectoryStrategy.FAIL); // throws if exists
// after
// Option A: overwrite
SkillUtils.syncToLocal(skill, baseDir, ExistingDirectoryStrategy.OVERWRITE);
// Option B: keep existing
try {
SkillUtils.syncToLocal(skill, baseDir, ExistingDirectoryStrategy.FAIL);
} catch (FileAlreadyExistsException e) {
// already present, nothing to do
} Defensive patterns
Strategy: validation
Validate before calling
Path skillDir = Paths.get(baseDir).resolve(dirName);
if (Files.exists(skillDir) && Files.isDirectory(skillDir)) {
// already synced — skip, or choose OVERWRITE/BACKUP
return;
}
SkillUtils.syncToLocal(skill, baseDir, skillDirName, ExistingDirectoryStrategy.FAIL); Type guard
static boolean skillDirMissing(String baseDir, String dirName) {
return !Files.isDirectory(Paths.get(baseDir).resolve(dirName));
} Try / catch
try {
SkillUtils.syncToLocal(skill, baseDir, ExistingDirectoryStrategy.FAIL);
} catch (FileAlreadyExistsException e) {
// idempotent: already present
} Prevention
- Use FAIL only when you need to detect pre-existing data; prefer OVERWRITE for idempotent re-syncs.
- Make sync operations idempotent by catching FileAlreadyExistsException as 'already done'.
- Check existence up front and branch rather than relying on the exception for control flow.
When it happens
Trigger: Calling syncToLocal with ExistingDirectoryStrategy.FAIL when {baseDir}/{skillName} (or {baseDir}/{skillDirName}) already exists as a directory.
Common situations: A previous sync created the directory; the same skill was synced twice; a directory was created out-of-band. The caller wanted to detect existing data rather than overwrite it.
Related errors
- Path escapes target directory: {target} is outside {baseDir}
- Invalid ZIP data: too short ({length} bytes)
- Invalid ZIP data: missing ZIP magic header (PK\x03\x04)
- Base directory cannot be blank
- Skill directory name cannot be blank
AI-assisted analysis of alibaba/nacos@9b989acdf1 (2026-08-14).
Data as JSON: /api/errors/a6f914cb56772ab3.
Report an issue: GitHub.