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

  1. Replace the symlink with a real copy of the target file before importing.
  2. Resolve the symlink manually with `readlink -f` and import the resolved target path instead.
  3. 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

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


AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12). Data as JSON: /api/errors/b36e217a2cee72b9. Report an issue: GitHub.