tauri-apps/tauri · error · Error
' ' is not a directory.
Error message
'${path}' is not a directory. What it means
Rollup's cleanDir helper opens a directory with opendirSync before unlinking its contents. ENOENT is treated as a no-op (nothing to clean), but ENOTDIR means the given path exists and is a file (or other non-directory), so cleaning would be meaningless and the build aborts with this error.
Solutions
- Remove or rename the file currently occupying the path that should be a directory
- Verify the output path passed to cleanDir is the intended directory and does not point at a file
- Re-run the build; if a tool keeps recreating a file there, fix that tool's output config
Example fix
// before rm -f dist // after rm -rf dist # ensure the path is a directory (or absent), not a plain file
Defensive patterns
Strategy: validation
Validate before calling
import { statSync } from 'node:fs';
function assertIsDirectoryOrAbsent(p: string) {
try { if (!statSync(p).isDirectory()) throw new Error(`${p} is not a directory`); }
catch (e: any) { if (e.code !== 'ENOENT') throw e; }
} Type guard
function isDirectory(p: string): boolean {
try { return statSync(p).isDirectory(); } catch { return false; }
} Try / catch
try { cleanDir(outputPath); } catch (err: any) {
if (err.message.includes('is not a directory')) {
rmSync(outputPath); cleanDir(outputPath); // remove offending file and retry
} else throw err;
} Prevention
- Keep build output paths dedicated to directories only
- Stat the output path before cleaning in build scripts
- Watch for symlinks that resolve to files
When it happens
Trigger: The cleanDir() function in packages/api/rollup.config.ts is called with a path that resolves to an existing regular file or symlink-to-file instead of a directory (e.g. a stale artifact, or outputDir pointing at a file).
Common situations: A previous build wrote a file where a directory was expected; someone replaced the dist/output directory with a file; a misconfigured output path in rollup.config.ts; a symlink pointing to a file rather than a directory.
Related errors
- does not exist
- Failed to canonicalize `WRY_ANDROID_KOTLIN_FILES_OUT_DIR`…
- failed to read plugin global API script
- is not a file
- unable to autogenerate core:default set
AI-assisted analysis of tauri-apps/tauri@460ec35447 (2026-09-18).
Data as JSON: /api/errors/5fba48b714c29d05.
Report an issue: GitHub.
Appendix: source
Thrown at packages/api/rollup.config.ts:104
writeBundle() {
// copy necessary files like `CHANGELOG.md` , `README.md` and Licenses to `./dist`
fg.sync('(LICENSE*|*.md|package.json)').forEach((f) =>
copyFileSync(f, `dist/${f}`)
)
}
}
}
function cleanDir(path: string) {
let dir: Dir
try {
dir = opendirSync(path)
} catch (err: any) {
switch (err.code) {
case 'ENOENT':
return // Noop when directory don't exists.
case 'ENOTDIR':
throw new Error(`'${path}' is not a directory.`)
default:
throw err
}
}
let file = dir.readSync()
while (file) {
const filePath = join(path, file.name)
rmSync(filePath, { recursive: true })
file = dir.readSync()
}
dir.closeSync()
}
View on GitHub (pinned to 460ec35447)