stablyai/orca · error · Error
Symlink not allowed in '${basename(srcPath)}'
Error message
Symlink not allowed in '${basename(srcPath)}' What it means
Thrown in copyLocalFileNoFollow during local import of a single file. The pre-open lstat (beforeOpenStat) detects the source path is a symbolic link. Local import policy forbids symlinks, so the copy aborts before opening the source handle.
Source
Thrown at src/main/ipc/filesystem-mutations.ts:715
if (statResult.isDirectory()) {
await recursiveCopyDir(srcPath, dstPath)
continue
}
if (!statResult.isFile()) {
throw new Error(`Unsupported file type in '${entry.name}'`)
}
await copyLocalFileNoFollow(srcPath, dstPath, statResult)
}
}
async function copyLocalFileNoFollow(
srcPath: string,
dstPath: string,
statResult?: Awaited<ReturnType<typeof lstat>>
): Promise<void> {
const beforeOpenStat = statResult ?? (await lstat(srcPath))
if (beforeOpenStat.isSymbolicLink()) {
throw new Error(`Symlink not allowed in '${basename(srcPath)}'`)
}
if (!beforeOpenStat.isFile()) {
throw new Error(`Unsupported file type in '${basename(srcPath)}'`)
}
let destinationCreated = false
const sourceHandle = await open(srcPath, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0))
let destinationHandle: Awaited<ReturnType<typeof open>> | null = null
try {
const openedStat = await sourceHandle.stat()
if (
!openedStat.isFile() ||
(typeof beforeOpenStat.size === 'number' && openedStat.size !== beforeOpenStat.size) ||
(typeof beforeOpenStat.ino === 'number' &&
beforeOpenStat.ino !== 0 &&
openedStat.ino !== 0 &&
openedStat.ino !== beforeOpenStat.ino) ||
(typeof beforeOpenStat.dev === 'number' &&View on GitHub (pinned to 1136503c6a)
Solutions
- Replace the symlink with a real copy of the target file before importing.
- Resolve the symlink manually with `readlink -f` and import the resolved target path instead.
- Use `cp -L` to create a dereferenced copy, then import that copy.
Example fix
// before: import /shortcut/config.json -> /real/config.json (symlink) // after: dereference and import the real file // cp -L /shortcut/config.json /tmp/config.json // import /tmp/config.json
Defensive patterns
Strategy: type-guard
Validate before calling
const { lstat } = await import('node:fs/promises')
async function assertNotSymlink(filePath: string): Promise<void> {
const st = await lstat(filePath)
if (st.isSymbolicLink()) {
throw new Error(`${filePath} is a symlink — dereference or remove before importing`)
}
} Type guard
async function isRegularFile(filePath: string): Promise<boolean> {
const { lstat } = await import('node:fs/promises')
try {
const st = await lstat(filePath)
return st.isFile() // false for symlinks, dirs, special files
} catch {
return false
}
} Try / catch
try {
await importLocalSource(sourcePath, destDir)
} catch (error) {
if (error instanceof Error && error.message.startsWith('Symlink not allowed')) {
showUserError(`${error.message} — replace with a real file copy.`)
return
}
throw error
} Prevention
- Check single-file sources with `test -L <path>` or `readlink <path>` before importing.
- Use `cp -L` to dereference symlinks into real copies before importing.
- In file pickers, filter out symlinks from selectable entries.
When it happens
Trigger: Importing a single top-level source file that is itself a symlink. The lstat on the resolved source path returns isSymbolicLink() true, and the function rejects it immediately.
Common situations: Dropping a symlinked file into the import target. Importing a file that is a symlink to a shared config or dotfile. A file manager creates a symlink instead of copying, and the user imports it.
Related errors
- Symlink not allowed in '${entry.name}'
- Path escaped upload root during staging: '${displayPath}'
- File changed during import: '${basename(srcPath)}'
- pet.json must not be a symlink.
- File changed during upload staging: '${displayPath}'
AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12).
Data as JSON: /api/errors/b36e217a2cee72b9.
Report an issue: GitHub.