stablyai/orca · error · Error
File changed during upload staging: '${displayPath}'
Error message
File changed during upload staging: '${displayPath}' What it means
Thrown in stageFileEntry after O_NOFOLLOW open. The code compares the post-open fstat (size, inode, device) against the pre-scan lstat result. If any field differs, the file was swapped or truncated between the lstat and the open. This pins the authorized file identity so that a race cannot substitute a different file body for the one that was validated.
Source
Thrown at src/main/ipc/filesystem-mutations.ts:604
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}'`)
}
if (
openedStat.size !== statResult.size ||
(statResult.ino !== 0 && openedStat.ino !== 0 && openedStat.ino !== statResult.ino) ||
(statResult.dev !== 0 && openedStat.dev !== 0 && openedStat.dev !== statResult.dev)
) {
throw new Error(`File changed during upload staging: '${displayPath}'`)
}
const totalBytes =
options?.totalBytesBefore === undefined
? openedStat.size
: options.totalBytesBefore + openedStat.size
assertRemoteUploadBudget(relativePath, openedStat.size, totalBytes)
const buffer = await fileHandle.readFile()
const afterReadStat = await fileHandle.stat()
if (afterReadStat.size !== openedStat.size) {
throw new Error(`File changed during upload staging: '${displayPath}'`)
}
return {
entry: {
relativePath: displayPath,
kind: 'file',
contentBase64: buffer.toString('base64')
},
byteLength: openedStat.sizeView on GitHub (pinned to 1136503c6a)
Solutions
- Re-run the staging when the source tree is quiescent — no concurrent writes.
- If the file is a log or output being actively written, snapshot it (copy to a stable temp location) before importing.
- On network filesystems, ensure the mount is stable and not remounting during the import.
Example fix
// before: stage a file that is being actively appended to // after: snapshot first, then stage the snapshot // cp /var/log/app.log /tmp/stable/app.log // import /tmp/stable/app.log
Defensive patterns
Strategy: retry
Try / catch
try {
await stageRemoteImport(sourcePath)
} catch (error) {
if (error instanceof Error && error.message.includes('File changed during upload staging')) {
// file was being written concurrently; retry after a brief pause
return stageRemoteImport(sourcePath) // one retry
}
throw error
} Prevention
- Do not stage files that are being actively written by running processes.
- Snapshot log files to a stable copy before staging.
- On network filesystems, verify mount stability before importing.
When it happens
Trigger: Between the initial lstat (line 577) and the O_NOFOLLOW open (line 593), a concurrent process replaces, truncates, or moves the file such that size, inode number, or device number changes. The open succeeds but fstat reports a different identity than the lstat.
Common situations: A log file being rotated during staging. A file being rewritten by an editor save or build step. NFS or network filesystems where inode/device mapping is unstable. Importing from a watched directory where file-watchers trigger modifications.
Related errors
- File changed during import: '${basename(srcPath)}'
- Path escaped upload root during staging: '${displayPath}'
- Destination file appeared before download completed
- '${relativePath}' is too large for remote import
- Remote import is too large
AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12).
Data as JSON: /api/errors/4a3229d2c82e36f0.
Report an issue: GitHub.