How to Fix Selenium Screenshot Permission Denied Errors on Linux
Fix Selenium screenshot permission errors on Linux by checking the output path, directory access, and runtime user, then verify the PNG was saved.
Selenium’s screenshot methods write PNG data to the filename your script supplies. On Linux, use an explicit absolute path in a directory the Selenium process can traverse and write to, then check the method’s Boolean return value and confirm the file exists. A permission error can come from the existing file, the directory where a new file would be created, or any directory in the path that lacks search permission.
This guide covers Python Selenium, which is what the title’s save_screenshot API refers to. The same Linux filesystem checks apply if your test runner uses another language binding.
1. Save to an explicit, writable path
Start with a known output directory and an absolute filename. Create the directory during test setup if it may not exist. Selenium’s API documents that save_screenshot returns False when an I/O error occurs, and recommends using a full path.
from pathlib import Path
from selenium import webdriver
output_dir = Path("/tmp/selenium-output")
output_dir.mkdir(parents=True, exist_ok=True)
screenshot_path = output_dir / "page.png"
driver = webdriver.Chrome()
try:
driver.get("https://example.com")
saved = driver.save_screenshot(str(screenshot_path))
if not saved:
raise OSError(f"Selenium could not save screenshot to {screenshot_path}")
if not screenshot_path.is_file():
raise FileNotFoundError(f"Screenshot is missing: {screenshot_path}")
print(f"Saved screenshot: {screenshot_path}")
finally:
driver.quit()
Replace /tmp/selenium-output with the directory intended for your test artifacts. The path is evaluated by the process running this Python code, so a relative path can point somewhere different in a shell, IDE, service, container, or CI worker.
2. Identify which permission check is failing
Linux reports access errors when the process cannot write the destination file, cannot create a file in its parent directory, or cannot traverse a directory in the path. Diagnose as the same effective user and in the same runtime environment that launches the test.
- Resolve the actual path. Print the absolute destination and the process working directory. Confirm the path is the one you expect.
- Check whether the file exists. If it exists, the process needs write access to that file. If it does not, it needs write access to the containing directory to create it.
- Check every directory component. The process needs search (execute) permission on each directory in the path, including ancestors. A writable final directory does not help if an earlier directory blocks traversal.
- Check the runtime identity. Inspect the effective user and group for the test process, not just your interactive login. In containers and CI, inspect the mounted output directory and identity inside that environment.
- Make the narrowest correction. Adjust ownership or grant the required access on the intended file or output directory. The right change depends on the owner, runtime identity, existing file, and path; a blanket recursive permission change is not a reliable diagnosis.
For a quick isolation check, save to a known writable absolute location such as a test-specific directory under /tmp. If that works, compare its owner and permissions with the original destination path.
3. Separate screenshot capture from local file writing
Selenium also exposes screenshot data as bytes or base64. If retrieving the image data works but writing it to the desired path fails, that points toward local path or permission access rather than the browser’s capture step.
from pathlib import Path
from selenium import webdriver
output = Path("/tmp/selenium-output")
output.mkdir(parents=True, exist_ok=True)
target = output / "page-from-bytes.png"
driver = webdriver.Chrome()
try:
driver.get("https://example.com")
png_bytes = driver.get_screenshot_as_png()
target.write_bytes(png_bytes)
print(f"Wrote {len(png_bytes)} bytes to {target}")
finally:
driver.quit()
This does not bypass filesystem permissions: write_bytes still needs permission to create or overwrite the target. It helps identify whether Selenium can produce screenshot data independently of its filename-saving helper.
4. Account for file creation defaults
If the PNG gets created but a later process cannot read it, inspect the process’s umask and the parent directory’s default ACL. The umask removes permission bits from newly created files, while a default ACL can affect the permissions applied at creation. These settings can explain downstream access failures; they are not the only possible cause.
Check the file’s resulting owner and permissions from the environment that consumes the artifact. Change the specific output directory’s ownership, ACL, or creation settings to match the intended producer and consumer identities.
5. cURL, Python, and Node.js notes
The failure described here is local file output from Selenium’s Python API, so there is no cURL equivalent for Selenium’s save_screenshot call. cURL and Node.js examples apply when using a remote screenshot API instead. The Python Selenium example above is the relevant runnable fix; the API alternatives below save a returned HTTP response to a local file, so that local destination still needs appropriate write permissions.
cURL with ScreenshotNeo
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://example.com \
-o shot.webp
Python with ScreenshotNeo
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://example.com"},
timeout=90,
)
r.raise_for_status()
with open("shot.webp", "wb") as f:
f.write(r.content)
Node.js with ScreenshotNeo
const q = new URLSearchParams({
access_key: 'YOUR_API_KEY',
url: 'https://example.com'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
For Selenium API behavior, see the Selenium Python WebDriver documentation. For ScreenshotNeo request parameters and options, see the ScreenshotNeo API documentation.
6. Troubleshooting common cases
| Symptom | Likely cause | What to check or change |
|---|---|---|
save_screenshot returns False |
An I/O error occurred while opening or writing the requested filename. | Use an absolute path; check whether the file exists, whether its parent permits creation, and whether all path directories are searchable by the runtime user. |
| It works locally but fails in CI | The CI process may run as another user, use another working directory, or see a differently mounted output path. | Log the absolute path and effective identity in the CI job; inspect permissions from inside the job’s container or worker. |
| The output directory exists but saving still fails | An ancestor directory may block traversal, or the existing PNG itself may not be writable. | Inspect every path component and the file owner/mode. Check the parent directory separately when the PNG does not yet exist. |
| The file appears, but an upload or later test cannot read it | Creation permissions may be affected by umask or a default ACL, or the consumer runs as a different identity. | Inspect the resulting file permissions, ACL, and reader identity; align the artifact directory’s access with both processes. |
| Saving bytes also raises a permission error | The failure is at local file creation or writing, not specific to Selenium’s screenshot filename helper. | Keep the screenshot bytes and write to a known writable absolute location; then correct access on the intended destination. |
| A relative path works in a terminal but not an IDE or service | The process working directory differs between launch methods. | Resolve and log the absolute path, or configure an explicit artifact directory. |
7. Reliability and runtime considerations
- Create artifact directories deliberately. Make output-directory creation part of setup so a missing directory is not mistaken for a screenshot capture failure.
- Keep destinations specific to the run. Concurrent tests that overwrite the same filename can replace each other’s artifacts. Use unique names or isolated run directories when tests run in parallel.
- Verify both status and artifact. Check Selenium’s Boolean result and the expected output path. For downstream jobs, also verify that the file is readable by the identity that consumes it.
- Keep permission changes scoped. Grant only the access needed on the artifact file or directory. The correct mode and owner depend on the environment and whether another process needs to read the screenshot.
8. Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server. A GET request returns a screenshot or PDF, so you do not have to install or manage a browser for this capture. See the API docs for options.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
- Cookie and consent banners are accepted like a visitor, and 60+ known consent platforms, newsletter popups, and chat widgets are removed before the shot; each step can be turned off.
- Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed. Response headers report the page verdict and billing status.
- An MCP server provides
take_screenshot,get_page_info, andcapture_pdffor AI agents and MCP clients. - The Free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots. Every feature is on every plan.
Create a free ScreenshotNeo account to get 1,000 screenshots a month with no card.
FAQ
Does Selenium throw an exception when it cannot save a screenshot?
The Python API documents a Boolean result and returns False for an I/O error. Check that result instead of assuming the file was written.
Why does permission denied happen when the directory looks writable?
The process may lack search permission on an ancestor directory, run as a different user, or be unable to overwrite an existing file. Check the full path from the runtime environment.
Can I fix this by changing the screenshot format?
The documented Selenium file-saving method writes PNG data. A format change does not resolve filesystem access checks; correct the destination path or permissions.
Where should CI jobs save screenshots?
Use an explicit artifact directory that the CI process can traverse and write, create it during setup, and confirm the downstream artifact consumer can read the resulting file.
Sources
- Selenium Python WebDriver API — screenshot methods, Boolean behavior, and full-path guidance.
- Linux open(2) — access errors when opening or creating files.
- Linux access(2) — directory search access along a path.
- Linux umask(2) — permission bits removed when files are created.


