shadcn-ui/ui · error · Error
Cannot write to ${filePath}: path exists and is a directory.
Error message
Cannot write to ${filePath}: path exists and is a directory. Please provide a file path instead. What it means
Thrown during file installation in update-files.ts when the resolved filePath already exists and statSync(filePath).isDirectory() is true. shadcn cannot overwrite a directory with a file, so it aborts with a clear message rather than producing a partial/corrupt write.
Source
Thrown at packages/shadcn/src/utils/updaters/update-files.ts:159
}
}
const existingFile = existsSync(filePath)
// TODO: revisit this when we implement utils transform instead of override.
if (
file.type === "registry:lib" &&
basename(file.path) === "utils.ts" &&
projectInfo?.framework.name === "laravel" &&
existingFile
) {
filesSkipped.push(path.relative(config.resolvedPaths.cwd, filePath))
continue
}
// Check if the path exists and is a directory - we can't write to directories.
if (existingFile && statSync(filePath).isDirectory()) {
throw new Error(
`Cannot write to ${filePath}: path exists and is a directory. Please provide a file path instead.`
)
}
// Run our transformers.
// Skip transformers for .env files to preserve exact content
// Skip transformers for universal item files (registry:file and registry:item)
// to preserve their original content as they're meant to be framework-agnostic
const isUniversalItemFile =
file.type === "registry:file" || file.type === "registry:item"
const content =
isEnvFile(filePath) || isUniversalItemFile
? file.content
: await transform(
{
filename: file.path,
raw: file.content,
config,View on GitHub (pinned to efac598707)
Solutions
- Inspect the filePath in the message; rename or remove the existing directory that occupies that path.
- Adjust the registry item's file.path or target so it does not collide with an existing folder.
- If the directory is intentional, change the item's path to write into a file inside it (e.g. "utils/index.ts").
- Re-run the add command after resolving the collision.
Example fix
# before — ./src/lib/utils exists as a directory, item wants utils.ts at ./src/lib/utils # rename the folder mv src/lib/utils src/lib/utils-dir # or fix the item path to utils/index.ts
Defensive patterns
Strategy: validation
Validate before calling
import fs from "fs"
function assertFileTargetIsNotDir(filePath: string) {
if (fs.existsSync(filePath) && fs.statSync(filePath).isDirectory()) {
throw new Error(`${filePath} is a directory`)
}
} Type guard
const isWritableFilePath = (p: string) => !fs.existsSync(p) || !fs.statSync(p).isDirectory()
Try / catch
try {
await updateFiles(tree, config, options)
} catch (e) {
if (e instanceof Error && /path exists and is a directory/.test(e.message)) {
// rename the conflicting directory and retry
}
throw e
} Prevention
- Avoid registry file paths that collide with existing directory names.
- When a name like "utils" is also a folder, write to "utils/index.ts" instead.
- Run a dry-run first to preview target paths before writing.
- Clean stale directories left by earlier partial installs.
When it happens
Trigger: For each file in the resolved tree, resolveFilePath computes the destination; if that path currently exists and is a directory (e.g. an item's file.path collides with an existing folder name), the guard throws before any transformer or write runs.
Common situations: A registry item's file path (e.g. "utils") collides with an existing directory of the same name; a previous install created a folder where a file is now expected; Laravel/Next.js routing folders like "[slug]" clashing; target alias resolving a file onto a directory.
Related errors
- Could not back up ${filePath}.
- Invalid target path "${target}". Target paths using @${alias
- Failed to read config at ${options.cwd}.
- Could not back up ${filePath}.
- Failed to sync linked workspace configs. ${error.message}
AI-assisted analysis of shadcn-ui/ui@efac598707 (2026-08-12).
Data as JSON: /api/errors/18e00989e12692e7.
Report an issue: GitHub.