Set Chrome’s download directory with an absolute path, then prevent collisions in your Python workflow. The download.default_directory preference chooses where Chrome saves files; it is not a documented “never overwrite” switch. For repeated downloads, use a separate directory per run or file, or wait for each download to finish and rename it before starting the next one.
ChromeDriver does not automatically wait for a download to complete, so do not quit the driver or move the file while Chrome is still writing it.
What actually causes the overwrite
Websites commonly return the same filename through the Content-Disposition response header. If every run saves report.pdf into one directory, the browser must resolve a name collision. Selenium’s standard Chrome preference only changes the destination folder:
options.add_experimental_option('prefs', {'download.default_directory': '/absolute/path'})
ChromeDriver documentation does not describe a general Selenium preference that selects overwrite, uniquify (for example, report (1).pdf), or prompt. Those conflict actions belong to Chrome’s extension downloads API, a different interface. Treat duplicate naming as application logic that your Python script controls.
#1 Best Overall
Configure a safe download directory
Use a real, writable directory with an absolute path. A task-specific folder prevents files from unrelated browser sessions being mistaken for your own downloads.
from pathlib import Path
from selenium import webdriver
# Resolve an ordinary, task-specific directory.
download_dir = Path('./downloads').resolve()
download_dir.mkdir(parents=True, exist_ok=True)
options = webdriver.ChromeOptions()
options.add_experimental_option(
'prefs',
{'download.default_directory': str(download_dir)},
)
driver = webdriver.Chrome(options=options)
try:
# Navigate and click a download link here.
driver.get('https://example.com')
finally:
driver.quit()
Pass the options object with webdriver.Chrome(options=options), as shown in current Selenium Python usage. Avoid Desktop and, on Linux, the home directory as the configured download folder; ChromeDriver lists such locations among directories that can be blocked, and those restrictions can change.
Choose a collision strategy
| Strategy | Original server name | Implementation effort | Best use |
|---|---|---|---|
| Directory per run or file | Preserved | Low | Parallel jobs, batch processing, or predictable isolation |
| Wait, then rename in Python | Preserved during download; final name is yours | Medium | One shared directory or a required naming convention |
| Intentional replacement after completion | Preserved until your explicit move | Low to medium | You really need only the newest copy |
DevTools allowAndName |
No; Chrome uses a download GUID | High | Advanced collision avoidance when GUID names are acceptable |
The first two approaches are the most portable. Separate directories eliminate collisions without depending on undocumented browser behavior. Renaming after completion gives you exact control over the final filename.
Use a directory for every download
Create a unique directory before clicking the link and associate that directory with the job. This is especially useful when several Selenium workers run at once; a worker cannot accidentally pick up another worker’s partial file.
Free tools Windows power users keep installed
One-click scans. No signup required.
Rank #2
from pathlib import Path
from uuid import uuid4
from selenium import webdriver
download_dir = (Path('./downloads') / uuid4().hex).resolve()
download_dir.mkdir(parents=True)
options = webdriver.ChromeOptions()
options.add_experimental_option(
'prefs', {'download.default_directory': str(download_dir)}
)
driver = webdriver.Chrome(options=options)
try:
driver.get('https://example.com')
# Locate and click the download link here.
# The completed file remains in download_dir under its server name.
finally:
driver.quit()
Wait for completion, then rename
Chrome normally writes an in-progress download with a temporary suffix such as .crdownload. A practical completion check waits for a new file to appear and for all temporary files to disappear. The check below also records the directory contents before the click, so it works when the server’s filename is not predictable.
from pathlib import Path
from time import monotonic, sleep
from selenium import webdriver
def wait_for_new_download(folder: Path, before: set[Path], timeout: float = 120) -> Path:
deadline = monotonic() + timeout
while monotonic() < deadline:
current = {p for p in folder.iterdir() if p.is_file()}
new_files = current - before
partials = [p for p in new_files if p.name.endswith('.crdownload')]
completed = [p for p in new_files if not p.name.endswith('.crdownload')]
if completed and not partials:
# If several files appeared, select the newest completed one.
return max(completed, key=lambda p: p.stat().st_mtime)
sleep(0.25)
raise TimeoutError(f'No completed download appeared within {timeout:g} seconds')
download_dir = Path('./downloads').resolve()
download_dir.mkdir(parents=True, exist_ok=True)
options = webdriver.ChromeOptions()
options.add_experimental_option(
'prefs', {'download.default_directory': str(download_dir)}
)
driver = webdriver.Chrome(options=options)
try:
driver.get('https://example.com')
before = {p for p in download_dir.iterdir() if p.is_file()}
# Replace this with the action that starts the download.
# driver.find_element(...).click()
downloaded = wait_for_new_download(download_dir, before)
final_path = download_dir / 'report-latest.pdf'
downloaded.replace(final_path)
finally:
driver.quit()
Do not rename until the temporary file has disappeared. For a known filename, you can poll that path directly, but still require that no .crdownload file remains. Set the timeout according to expected file size and network conditions; the 120-second value above is an example, not a Chrome guarantee.
Make replacement explicit
If the business rule is “keep only the latest report,” download into the task folder, wait for completion, and then replace the previous file with Python. An explicit Path.replace() documents the destructive operation and keeps it out of Chrome’s undocumented collision handling.
Advanced option: GUID-based download names
The Chrome DevTools Protocol documents an experimental Browser.setDownloadBehavior mode named allowAndName. It requires a download path and names files with download GUIDs, which avoids same-name collisions at the saved-file level. The trade-off is that the convenient server-provided filename is lost, and the behavior is experimental.
Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →from pathlib import Path
from selenium import webdriver
download_dir = Path('./downloads').resolve()
download_dir.mkdir(parents=True, exist_ok=True)
options = webdriver.ChromeOptions()
driver = webdriver.Chrome(options=options)
try:
driver.execute_cdp_cmd(
'Browser.setDownloadBehavior',
{'behavior': 'allowAndName', 'downloadPath': str(download_dir)},
)
driver.get('https://example.com')
# Trigger the download here.
finally:
driver.quit()
Validate this against the exact Chrome and Selenium versions used by your application before making it a dependency. The official Selenium Chromium options API also exposes an enable_downloads property, but its documented purpose is session download capability, not duplicate-name selection.
Do not confuse Selenium with the extension downloads API
Chrome’s extension API has a conflictAction setting with uniquify, overwrite, and prompt values. That setting controls downloads initiated through an extension. It is not a standard Selenium Python preference, so adding a similarly named key to ChromeOptions does not reliably stop ordinary WebDriver downloads from overwriting files.
Reliable end-to-end pattern
- Create an absolute, writable directory. Prefer one directory per run or per file.
- Configure it before creating the driver. Use
add_experimental_option('prefs', {'download.default_directory': ...}). - Record the directory contents. This lets you identify an unknown server filename.
- Trigger the download. Keep the browser alive.
- Wait for completion. Require a completed file and no temporary partial file, with a timeout.
- Rename or move in Python. Choose a unique, meaningful destination, or deliberately replace the previous artifact.
- Quit only after the file is safely handled. ChromeDriver does not automatically wait for completion.
Troubleshooting
The file still appears in the old folder
Print the resolved path and verify that it exists and is writable. Ensure the preference is attached to the same ChromeOptions instance passed to webdriver.Chrome, and configure it before launching the driver. Also check that the site is opening a second browser or an external application rather than downloading in the configured session.
Chrome creates file (1).pdf when I wanted a controlled name
That name is browser conflict handling, not a Selenium naming policy. Use a separate directory, or wait for completion and rename the new file yourself. If you need replacement, call Path.replace() after completion.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
The script quits before the file is usable
Move driver.quit() after your completion check. A click returning only means navigation or the download request started; it does not mean the bytes have been written.
The wait times out
- Confirm the click actually starts a download rather than opening a viewer tab.
- Increase the timeout for large files or slow networks.
- Check permissions and free space in the destination directory.
- Look for a lingering
.crdownloadfile, which indicates an incomplete transfer. - Capture the directory listing before and after the click; some servers choose a filename you did not expect.
Parallel workers pick up one another’s files
Do not share one download directory between workers. Give each session a unique directory and pass the resulting path back with the job result. This also avoids races when two responses use the same server filename.
The session behaves unexpectedly after an upgrade
Keep Chrome and ChromeDriver on matching major versions, as Selenium’s Chrome guidance requires. Recheck experimental DevTools behavior after browser upgrades.
Performance and reliability considerations
- Polling: A 250-millisecond interval is usually responsive without busy-spinning; choose a value appropriate for your workload.
- Timeouts: Base them on expected transfer size and network variability. Always fail clearly rather than silently proceeding with a partial file.
- Atomic handoff: Rename only after completion so downstream code never consumes a file still being written.
- Cleanup: Remove per-job directories after your pipeline has verified and archived the result.
- Observability: Log the resolved directory, the detected filename, elapsed time, and final path. These details make intermittent failures diagnosable.
Or skip the browser setup
If what you actually need is an image or PDF of a webpage—not the site’s downloadable report—ScreenshotNeo can capture the URL with one request. It is not a replacement for downloading arbitrary files, but it avoids maintaining a Selenium browser for page captures. Cookie and consent banners are accepted and removed before capture, along with more than 60 known consent platforms, newsletter popups, and chat widgets; bot checks, blank pages, timeouts, failed loads, and cache hits are not billed as clean shots. Its MCP server can let Claude, Cursor, or another MCP client call take_screenshot, get_page_info, or capture_pdf.
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Clear out junk files and repair common Windows errors3Fix the driver behind crashes, sound loss and screen glitchescURL:
curl -G 'https://api.screenshotneo.com/v1/shot' -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python:
import requests
r = requests.get(
'https://api.screenshotneo.com/v1/shot',
params={'access_key': 'YOUR_API_KEY', 'url': 'https://stripe.com'},
timeout=90,
)
open('shot.webp', 'wb').write(r.content)
Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
See the ScreenshotNeo documentation for options and response headers. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000. Create a free ScreenshotNeo account.
Best Value
FAQ
Can I force Chrome to ask me for a filename on every Selenium download?
You can configure Chrome’s normal user-facing download prompt, but that introduces UI interaction and is unsuitable for unattended automation. Deterministic Python-side naming is more reliable.
Should I use a file lock when several processes download at once?
A lock can coordinate a shared directory, but separate directories per worker are simpler because they remove the collision and identification problem entirely.
Does the technique work for files generated by JavaScript?
Yes, provided the browser session ultimately creates a normal download. Wait for the resulting file in the configured directory; if the script instead receives bytes through an API response, save those bytes directly with Python.
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Frequently Asked Questions
Why does setting download.default_directory not stop duplicate filenames?
That preference selects the destination directory only. Selenium does not document it as a duplicate-name policy, so collision handling must be isolated with directories or implemented after completion in Python.
What is the safest approach for parallel Selenium jobs?
Create a unique absolute download directory for each run or file, then wait for completion before returning the path.
When should I consider Browser.setDownloadBehavior with allowAndName?
Only when GUID-based filenames are acceptable and you have validated the experimental DevTools behavior with your specific Chrome and Selenium versions.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.
Recommended Free Tools

