{"record":{"id":"7ac4967a4f54022b","repo":"alibaba/nacos","slug":"failed-to-delete-path","errorCode":null,"errorMessage":"Failed to delete: {path}","messagePattern":"Failed to delete: (.+?)","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"api/src/main/java/com/alibaba/nacos/api/ai/model/skills/SkillUtils.java","lineNumber":522,"sourceCode":"    /**\n     * Recursively delete a directory and all its contents.\n     *\n     * @param directory the directory to delete\n     * @throws IOException if deletion fails\n     */\n    private static void deleteDirectory(Path directory) throws IOException {\n        if (!Files.exists(directory)) {\n            return;\n        }\n        \n        // Delete files before directories\n        Files.walk(directory)\n            .sorted((a, b) -> b.compareTo(a))\n            .forEach(path -> {\n                try {\n                    Files.delete(path);\n                } catch (IOException e) {\n                    throw new RuntimeException(\"Failed to delete: \" + path, e);\n                }\n            });\n    }\n    \n    /**\n     * Main config dataId for skill.\n     *\n     * @deprecated No longer used. Replaced by {@link #SKILL_INDEX_DATA_ID} for the manifest\n     *             and versioned resource files for content.\n     */\n    @Deprecated\n    public static final String SKILL_MAIN_DATA_ID = \"skill.json\";\n    \n    /**\n     * Resource config dataId prefix.\n     */\n    public static final String RESOURCE_DATA_ID_PREFIX = \"resource_\";\n    ","sourceCodeStart":504,"sourceCodeEnd":540,"githubUrl":"https://github.com/alibaba/nacos/blob/9b989acdf181d00898f2e8839257bb2b2a3cefe3/api/src/main/java/com/alibaba/nacos/api/ai/model/skills/SkillUtils.java#L504-L540","documentation":"Thrown by the private deleteDirectory helper as a RuntimeException wrapping an IOException that occurred during Files.delete inside a Files.walk traversal. It is a fatal abort of the recursive directory deletion used by the OVERWRITE/BACKUP sync strategies when cleaning up a temp or existing skill directory.","triggerScenarios":"Any Files.delete(path) inside the walk throws IOException (file in use, permission denied, path vanished between walk and delete) and it is rethrown as RuntimeException(\"Failed to delete: \" + path, e).","commonSituations":"On Windows a file is still open/locked by another process; a file is read-only and permissions deny deletion; an antivirus or indexer holds a handle; a race condition removed the file between the walk snapshot and the delete.","solutions":["On Windows, ensure no process (editor, indexer, AV) holds the directory open before syncing.","Retry the sync after closing any handles to files under the skill directory.","Grant the process delete/write permissions on the base directory.","Use OVERWRITE only when the directory is quiescent; consider BACKUP to avoid deleting a locked tree."],"exampleFix":"// before (caller)\nSkillUtils.syncToLocal(skill, baseDir, ExistingDirectoryStrategy.OVERWRITE);\n// -> RuntimeException: Failed to delete: ... (file locked)\n\n// after (caller)\ntry {\n    SkillUtils.syncToLocal(skill, baseDir, ExistingDirectoryStrategy.OVERWRITE);\n} catch (RuntimeException e) {\n    if (e.getCause() instanceof IOException) {\n        log.warn(\"Skill dir locked, retrying with BACKUP\");\n        SkillUtils.syncToLocal(skill, baseDir, ExistingDirectoryStrategy.BACKUP);\n    } else throw e;\n}","handlingStrategy":"try-catch","validationCode":"// Ensure the directory is not locked before syncing (Windows)\nPath skillDir = Paths.get(baseDir).resolve(skill.getName());\nif (Files.exists(skillDir)) {\n    try (var stream = Files.walk(skillDir)) {\n        // touch each file to confirm no lock\n        stream.forEach(p -> {});\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    SkillUtils.syncToLocal(skill, baseDir, ExistingDirectoryStrategy.OVERWRITE);\n} catch (RuntimeException e) {\n    if (e.getCause() instanceof IOException io) {\n        log.warn(\"Directory locked, falling back to BACKUP: {}\", io.getMessage());\n        SkillUtils.syncToLocal(skill, baseDir, ExistingDirectoryStrategy.BACKUP);\n    } else {\n        throw e;\n    }\n}","preventionTips":["On Windows, close all handles (editors, indexers) under the skill dir before syncing.","Prefer BACKUP on platforms where files are frequently locked.","Run syncs when the directory is quiescent (no active readers).","Grant the process full write/delete permissions on the base directory."],"tags":["java","nacos","ai","skills","filesystem","io","windows"],"backgroundTag":null,"analyzedSha":"9b989acdf181d00898f2e8839257bb2b2a3cefe3","analyzedAt":"2026-08-14T07:17:31.569Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}