koodo-reader/koodo-reader · error · Error
Failed to create directory: ${part}
Error message
Failed to create directory: ${part} What it means
ensureDirectoryExists() walks each path segment of folderPath, calling getDirectoryHandle(part, {create:true}) on the current handle. If any segment cannot be created or opened, it logs the underlying error and rethrows this sanitized 'Failed to create directory: <part>' error naming the offending segment. Common underlying causes are invalid characters in the segment name, name collisions with an existing file, or OS-level permission failures.
Source
Thrown at src/utils/file/localFile.ts:312
const pathParts = normalizedPath
.split("/")
.filter((part) => part.length > 0);
let currentHandle = baseHandle;
for (const part of pathParts) {
try {
// 尝试获取现有文件夹
currentHandle = await currentHandle.getDirectoryHandle(part);
} catch (error) {
// 如果文件夹不存在,创建新文件夹
try {
currentHandle = await currentHandle.getDirectoryHandle(part, {
create: true,
});
} catch (createError) {
console.error(`Error creating directory ${part}:`, createError);
throw new Error(`Failed to create directory: ${part}`);
}
}
}
return currentHandle;
}
// 保存文件到本地目录(支持指定文件夹)
static async saveFile(
filename: string,
content: string | ArrayBuffer,
folderPath?: string
): Promise<boolean> {
try {
const directoryHandle = await this.getStoredDirectoryHandle();
if (!directoryHandle) {
throw new Error("No directory access permission");
}View on GitHub (pinned to 7d40df41e0)
Solutions
- Sanitize folderPath segments before calling saveFile: strip reserved characters (<>:"/\\|?*), trim, and collapse empty segments
- Split the path yourself and create directories one level at a time to identify the failing segment
- Check the console.error line above the throw — it logs the original createError with the real OS reason
- Ensure the segment does not collide with an existing file of the same name in the parent directory
Example fix
// before
await LocalFileService.saveFile(content, fileName, `books/${book.title}/covers`);
// after
const safeTitle = book.title.replace(/[<>:"/\\|?*]/g, "_").trim();
const folderPath = ["books", safeTitle, "covers"].filter(Boolean).join("/");
await LocalFileService.saveFile(content, fileName, folderPath); Defensive patterns
Strategy: validation
Validate before calling
function sanitizeFolderPath(p: string): string {
return p
.split("/")
.map((seg) => seg.replace(/[<>:"\\|?*\x00-\x1f]/g, "_").trim())
.filter(Boolean)
.join("/");
}
// usage: saveFile(content, name, sanitizeFolderPath(rawPath)) Type guard
function isSafeSegment(seg: string): boolean {
return seg.length > 0 && seg.length < 255 && !/[<>:"\\|?*\/\x00-\x1f]/.test(seg);
} Try / catch
try {
await LocalFileService.saveFile(content, name, path);
} catch (e) {
if (e.message.startsWith("Failed to create directory:")) {
const badPart = e.message.split(":")[1]?.trim();
console.warn("sanitize and retry for segment:", badPart);
} else throw e;
} Prevention
- Sanitize every path segment (reserved chars, length) before saveFile
- Filter out empty segments caused by double slashes
- Avoid deriving folder names raw from book titles/authors
- Check the console.error log above the throw for the real OS-level cause (e.g. name collision with an existing file)
When it happens
Trigger: Calling saveFile(content, fileName, folderPath) where folderPath contains a segment with filesystem-illegal characters (e.g. ':' or '\\' on Windows, '/' mid-segment), a segment name matching an existing FILE in the parent directory, an empty segment from a malformed path like 'a//b', or a name too long for the OS.
Common situations: Folder paths derived from book titles/authors containing reserved characters, nested paths built by string concatenation with accidental double slashes, or saving into a synced/locked directory (OneDrive, network share) that rejects creation.
Related errors
- Invalid custom translation plugin
- Invalid custom dictionary plugin
- Unsupported file format: ${contentType}
- Invalid path
- Cannot open cover
AI-assisted analysis of koodo-reader/koodo-reader@7d40df41e0 (2026-08-29).
Data as JSON: /api/errors/c06e65bc4bd1da46.
Report an issue: GitHub.