stablyai/orca · error · Error
Could not choose a unique file name in Downloads.
Error message
Could not choose a unique file name in Downloads.
What it means
Thrown by BrowserDownloadDestinationReservations.reserve() after exhausting MAX_BROWSER_DOWNLOAD_COLLISION_ATTEMPTS (1000) candidates without finding a unique filename. For each attempt it builds a candidate (filename, filename (1), filename (2), ...) and checks if the reservation key is already held OR if the path exists on disk. If all 1000 are taken, it gives up. This is a plain Error (not BrowserError) indicating the Downloads directory is saturated with identically-named files.
Source
Thrown at src/main/browser/browser-download-destination.ts:86
const safeFilename = normalizeFilename(filename)
const downloadsPath = this.downloadsPath()
for (let attempt = 0; attempt < MAX_BROWSER_DOWNLOAD_COLLISION_ATTEMPTS; attempt += 1) {
const candidateFilename = buildCollisionCandidate(safeFilename, attempt)
const savePath = path.join(downloadsPath, candidateFilename)
const reservationKey = normalizeReservationKey(savePath, this.platform)
if (this.reservedPathKeys.has(reservationKey) || this.pathExists(savePath)) {
continue
}
this.reservedPathKeys.add(reservationKey)
return {
filename: candidateFilename,
savePath,
reservationKey
}
}
throw new Error('Could not choose a unique file name in Downloads.')
}
release(reservationKey: string | null): void {
if (!reservationKey) {
return
}
this.reservedPathKeys.delete(reservationKey)
}
clear(): void {
this.reservedPathKeys.clear()
}
}
export const browserDownloadDestinationReservations = new BrowserDownloadDestinationReservations()
View on GitHub (pinned to 1136503c6a)
Solutions
- Clean up the Downloads directory — remove or archive old collision-suffixed files so candidates are free.
- Ensure release(reservationKey) is called when each download completes or is cancelled, so reservedPathKeys doesn't grow unboundedly.
- If this is a reservation leak, audit the download lifecycle (WillDownload → completed/cancelled) to confirm release() is always invoked.
- Call clear() on the reservations instance if the reserved set is known to be stale (e.g. after app restart where in-flight downloads didn't persist).
- If legitimately downloading many same-named files, pre-rename the source or destination to avoid relying on collision suffixing beyond 1000.
Example fix
// before — reservations leak if download is cancelled without release
const dest = reservations.reserve('report.pdf')
// ... download starts, user cancels, release() never called
// after — always release in a finally block
const dest = reservations.reserve('report.pdf')
try {
await performDownload(dest.savePath)
} finally {
reservations.release(dest.reservationKey)
} Defensive patterns
Strategy: validation
Validate before calling
import { existsSync } from 'node:fs'
import path from 'node:path'
function canReserve(filename: string, downloadsPath: string, reserved: Set<string>): boolean {
for (let i = 0; i < 1000; i++) {
const candidate = i === 0 ? filename : `${path.parse(filename).name} (${i})${path.extname(filename)}`
const p = path.join(downloadsPath, candidate)
if (!reserved.has(p.toLowerCase()) && !existsSync(p)) return true
}
return false
} Try / catch
try {
return reservations.reserve(filename)
} catch (e) {
if (e instanceof Error && /unique file name/i.test(e.message)) {
reservations.clear() // drop stale reservations and retry
return reservations.reserve(filename)
}
throw e
} Prevention
- Always call release(reservationKey) in a finally block when a download completes or cancels
- Periodically clean the Downloads directory of old collision-suffixed files
- Call clear() on app startup to drop stale reservations from a previous session
- Audit the download lifecycle to ensure release() is invoked on both success and cancellation paths
When it happens
Trigger: reserve(filename) loops attempt 0..999; each candidate's savePath either already exists on disk (pathExists) or its reservationKey is in reservedPathKeys (another in-flight download claimed it). If 1000 consecutive candidates collide, the loop exits and throws. Happens when the Downloads dir already has 'file.txt' through 'file (999).txt' OR when reservedPathKeys holds 1000 keys for the same stem (1000 concurrent downloads of the same filename).
Common situations: A Downloads directory with hundreds of identically-named downloaded files accumulated over time (no cleanup); a burst of concurrent downloads all named the same (e.g. programmatic download of 'report.pdf' x1000); a bug where reservations are never released (release() not called on completion/cancel) causing the reserved set to grow unboundedly; a pathExists mock in tests that always returns true.
Related errors
- [plain-node-entry-guard] could not smoke-load daemon-entry.j
- Missing packaged resources directory: ${resourcesDir}
- Packaged node-pty is missing ${conptyRoot}
- Packaged node-pty is missing ${sourceFile}
- bin.orca target is not a file: ${binTarget}
AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12).
Data as JSON: /api/errors/d1d6edabc8ace4a9.
Report an issue: GitHub.