stablyai/orca · warning · RuntimeUploadSymlinkError

Symlink not allowed in '${displayPath}'

Error message

Symlink not allowed in '${displayPath}'

What it means

Thrown as RuntimeUploadSymlinkError by stageFileEntry() when a staged single file lstat()s as a symbolic link. stageFileEntry is used both for child files during directory staging and for a single-file source in stageOneSourceForRuntimeUpload. The dedicated error class is caught upstairs and converted to { status: 'skipped', reason: 'symlink' }, so a symlinked top-level file is skipped rather than having its target uploaded.

Source

Thrown at src/main/ipc/filesystem-mutations.ts:580

      })
      totalBytes += stagedFile.byteLength
      entries.push(stagedFile.entry)
    }
  }

  await visit(rootPath)
  return entries
}

async function stageFileEntry(
  filePath: string,
  relativePath: string,
  options?: { rootRealPath?: string; totalBytesBefore?: number }
): Promise<{ entry: StagedExternalImportEntry; byteLength: number }> {
  const statResult = await lstat(filePath)
  const displayPath = normalizeRelativeUploadPath(relativePath)
  if (statResult.isSymbolicLink()) {
    throw new RuntimeUploadSymlinkError(`Symlink not allowed in '${displayPath}'`)
  }
  if (!statResult.isFile()) {
    throw new Error(`Unsupported file type in '${displayPath}'`)
  }
  if (options?.rootRealPath) {
    await assertRealPathInsideRoot(options.rootRealPath, filePath, displayPath)
  }
  const initialTotalBytes =
    options?.totalBytesBefore === undefined
      ? statResult.size
      : options.totalBytesBefore + statResult.size
  assertRemoteUploadBudget(relativePath, statResult.size, initialTotalBytes)
  const fileHandle = await open(filePath, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0))
  try {
    const openedStat = await fileHandle.stat()
    if (!openedStat.isFile()) {
      throw new Error(`Unsupported file type in '${displayPath}'`)
    }

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Select the real target file instead of the symlink, or copy the target content to a regular file and upload that.
  2. Resolve symlinks in the selection UI before staging so only regular files are passed.
  3. Treat the skipped result as expected and inform the user that symlinked files are not uploaded.
Defensive patterns

Strategy: try-catch

Validate before calling

// Detect a symlinked single-file source before staging
import { lstat } from 'node:fs/promises'
async function isSymlinkFile(p: string): Promise<boolean> {
  try { return (await lstat(p)).isSymbolicLink() } catch { return false }
}

Try / catch

const res = await stageOneSourceForRuntimeUpload(src)
if (res.status === 'skipped' && res.reason === 'symlink') {
  // tell the user the selected file is a symlink; offer to upload its real target
}

Prevention

When it happens

Trigger: A single-file source passed to runtime-upload staging lstat()s as a symlink (isSymbolicLink()), or a child file staged from a directory is a symlink; stageFileEntry rejects it before opening.

Common situations: User selected a symlinked file (a linked shortcut) for upload; a tree contains symlinked dotfiles or linked assets; tooling created symlinks to shared files that ended up in the selection.

Related errors


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