{"record":{"id":"535674b4b5a00a89","repo":"stablyai/orca","slug":"upload-source-changed-while-being-inspected-sou","errorCode":null,"errorMessage":"Upload source changed while being inspected: ${sourcePath}","messagePattern":"Upload source changed while being inspected: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/ipc/filesystem-import-ssh-directory.ts","lineNumber":17,"sourceCode":"import { lstat, readdir, realpath } from 'node:fs/promises'\nimport { isAbsolute, join, relative, sep } from 'node:path'\nimport type { FileUploadSession, IFilesystemProvider } from '../providers/types'\nimport { assertSafeRemotePathSegment, type RemotePathFlavor } from '../ssh/ssh-remote-platform'\n\nexport async function captureLocalUploadRoot(\n  sourcePath: string,\n  sourceStat: Awaited<ReturnType<typeof lstat>>\n): Promise<string> {\n  const rootRealPath = await realpath(sourcePath)\n  const rootRealStat = await lstat(rootRealPath)\n  if (\n    statIdentityPartChanged(sourceStat.ino, rootRealStat.ino) ||\n    statIdentityPartChanged(sourceStat.dev, rootRealStat.dev) ||\n    !rootRealStat.isDirectory()\n  ) {\n    throw new Error(`Upload source changed while being inspected: ${sourcePath}`)\n  }\n  return rootRealPath\n}\n\nexport async function preScanSshImportDirectory(\n  dirPath: string,\n  remotePathFlavor: RemotePathFlavor\n): Promise<boolean> {\n  const entries = await readdir(dirPath, { withFileTypes: true })\n  for (const entry of entries) {\n    assertSafeRemotePathSegment(entry.name, remotePathFlavor)\n    if (entry.isSymbolicLink()) {\n      return true\n    }\n    if (entry.isDirectory()) {\n      const childPath = join(dirPath, entry.name)\n      if (await preScanSshImportDirectory(childPath, remotePathFlavor)) {\n        return true","sourceCodeStart":1,"sourceCodeEnd":35,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/src/main/ipc/filesystem-import-ssh-directory.ts#L1-L35","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Re-select the source directory and retry the import promptly, without modifying it during the scan.","Pause build/watchdog processes that may rewrite the directory during import.","If on a network filesystem with unstable ino/dev, copy the source to a local stable path first and import that."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Re-validate identity immediately before captureLocalUploadRoot\nimport { lstat, realpath } from 'node:fs/promises'\nasync function isStableDir(p: string, prev: { ino: number; dev: number }): Promise<boolean> {\n  const real = await realpath(p)\n  const s = await lstat(real)\n  return s.isDirectory() && (prev.ino === 0 || s.ino === 0 || s.ino === prev.ino) &&\n         (prev.dev === 0 || s.dev === 0 || s.dev === prev.dev)\n}","typeGuard":null,"tryCatchPattern":"try {\n  await importExternalPathsSsh([src], dest, connId, opts)\n} catch (e) {\n  if (e instanceof Error && /changed while being inspected/.test(e.message)) {\n    // re-select the source and retry once it is stable\n  } else throw e\n}","preventionTips":["Do not modify the selected source directory while the import scan runs.","On network filesystems with unstable ino/dev, copy the source to a local stable path first.","Pause build/watch tools that rewrite the directory during import."],"tags":["filesystem","toctou","ssh","upload","validation"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}