stablyai/orca · error · Error

Upload source changed while being inspected: ${sourcePath}

Error message

Upload source changed while being inspected: ${sourcePath}

What it means

Thrown by captureLocalUploadRoot() during SSH directory import. It compares the caller-supplied sourceStat (lstat of the unresolved source) against a fresh lstat of realpath(source). If the inode (ino) or device (dev) identity parts both read as known-and-different, or the resolved real path is no longer a directory, the source is treated as replaced mid-inspection. This is a TOCTOU guard: between the initial lstat and the realpath+lstat the directory was swapped, moved, bind-mounted, or changed type.

Source

Thrown at src/main/ipc/filesystem-import-ssh-directory.ts:17

import { lstat, readdir, realpath } from 'node:fs/promises'
import { isAbsolute, join, relative, sep } from 'node:path'
import type { FileUploadSession, IFilesystemProvider } from '../providers/types'
import { assertSafeRemotePathSegment, type RemotePathFlavor } from '../ssh/ssh-remote-platform'

export async function captureLocalUploadRoot(
  sourcePath: string,
  sourceStat: Awaited<ReturnType<typeof lstat>>
): Promise<string> {
  const rootRealPath = await realpath(sourcePath)
  const rootRealStat = await lstat(rootRealPath)
  if (
    statIdentityPartChanged(sourceStat.ino, rootRealStat.ino) ||
    statIdentityPartChanged(sourceStat.dev, rootRealStat.dev) ||
    !rootRealStat.isDirectory()
  ) {
    throw new Error(`Upload source changed while being inspected: ${sourcePath}`)
  }
  return rootRealPath
}

export async function preScanSshImportDirectory(
  dirPath: string,
  remotePathFlavor: RemotePathFlavor
): Promise<boolean> {
  const entries = await readdir(dirPath, { withFileTypes: true })
  for (const entry of entries) {
    assertSafeRemotePathSegment(entry.name, remotePathFlavor)
    if (entry.isSymbolicLink()) {
      return true
    }
    if (entry.isDirectory()) {
      const childPath = join(dirPath, entry.name)
      if (await preScanSshImportDirectory(childPath, remotePathFlavor)) {
        return true

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Re-select the source directory and retry the import promptly, without modifying it during the scan.
  2. Pause build/watchdog processes that may rewrite the directory during import.
  3. If on a network filesystem with unstable ino/dev, copy the source to a local stable path first and import that.
Defensive patterns

Strategy: retry

Validate before calling

// Re-validate identity immediately before captureLocalUploadRoot
import { lstat, realpath } from 'node:fs/promises'
async function isStableDir(p: string, prev: { ino: number; dev: number }): Promise<boolean> {
  const real = await realpath(p)
  const s = await lstat(real)
  return s.isDirectory() && (prev.ino === 0 || s.ino === 0 || s.ino === prev.ino) &&
         (prev.dev === 0 || s.dev === 0 || s.dev === prev.dev)
}

Try / catch

try {
  await importExternalPathsSsh([src], dest, connId, opts)
} catch (e) {
  if (e instanceof Error && /changed while being inspected/.test(e.message)) {
    // re-select the source and retry once it is stable
  } else throw e
}

Prevention

When it happens

Trigger: importExternalPathsSsh is called for a directory source; between the initial lstat and captureLocalUploadRoot the directory is renamed/replaced, or its realpath now resolves to a non-directory, or the filesystem (network/macOS fid instability) reports a different ino/dev.

Common situations: Another process or the user moved/replaced the dropped directory while the import dialog/scan was running; a build tool regenerated the directory; network filesystems where ino/dev are not stable across calls; the source was a symlink target that changed underneath.

Related errors


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