ScreenshotNeo

BlogScreenshots on your device

Why Python Screenshots Fail on Some PCs and How to Fix Them

Find why Python screenshots fail across Windows, macOS and Linux, then fix display access, dependencies, scaling, permissions and policies.

By the ScreenshotNeo team1 October 20267 min read

Short answer: Python screenshots fail because the process cannot access the interactive display, the operating system uses a different capture backend, Linux dependencies are missing, a managed-device policy blocks capture, or crop coordinates use the wrong scale. Python itself is rarely the root cause.

This guide uses Pillow’s ImageGrab as a documented example. Other libraries may use different native APIs. Check the Pillow ImageGrab reference for the installed release.

1. Run the smallest possible test

Run this with the same interpreter and launch context as the failing application.

import os
import platform
import sys
from pathlib import Path
from PIL import ImageGrab

print('Python:', sys.executable)
print('Version:', sys.version)
print('OS:', platform.platform())
print('DISPLAY:', os.environ.get('DISPLAY'))
print('WAYLAND_DISPLAY:', os.environ.get('WAYLAND_DISPLAY'))

try:
    image = ImageGrab.grab()
    print('Captured:', image.mode, image.size)
    output = Path('screen-test.png').resolve()
    image.save(output)
    print('Saved:', output)
except Exception as exc:
    print(type(exc).__name__ + ':', exc)
    raise
  • If capture and saving succeed, investigate cropping, timing or window targeting.
  • If capture fails, investigate display access, dependencies, the backend and policy.
  • If capture succeeds but saving fails, fix the path or filesystem permissions separately.

Record the Python version, package and version, OS, complete exception, and whether the process runs from a desktop terminal, remote shell, service, container or CI job. Confirm that sys.executable is the interpreter where the package was installed.

2. Why the same script works on one PC

The process is in a different desktop session

A terminal inside a logged-in desktop can access a display that an SSH session, service, scheduled task, container or CI worker cannot. A headless process may have no interactive desktop at all. Check the launch context before reinstalling Python.

The package uses an OS-specific backend

ImageGrab is a convenience API over platform-specific capture paths. Pillow documents Windows, macOS and Linux behavior separately. Compare package versions on the working and failing machines because backend behavior can change between releases.

Linux session and helper tools differ

Pillow’s documented Linux route uses X11/XCB support. When the default X11 capture does not return an image and no explicit display is supplied, Pillow documents fallback checks for gnome-screenshot, grim and spectacle.

Inspect DISPLAY, WAYLAND_DISPLAY, the active desktop session, sandboxing and access to the logged-in user’s graphical session. Wayland is not a universal failure, and installing one utility is not a universal fix.

For sandboxed Linux applications, the XDG Desktop Portal screenshot interface supports screen, window, area and active-window targets. A Python library does not automatically use the portal; verify integration first.

macOS Retina changes dimensions

Pillow documents that Retina captures are 2x by default and that scale_down=True requests a 1x result. Inspect the actual image dimensions before changing a crop.

from PIL import ImageGrab

image = ImageGrab.grab()
print('Physical pixels:', image.size)

one_x = ImageGrab.grab(scale_down=True)
print('1x pixels:', one_x.size)

Do not blindly double every coordinate. Establish whether the code uses logical points or physical pixels.

Windows monitor layout and policy differ

Pillow documents all_screens=True for multiple monitors. The virtual desktop can have a negative top-left coordinate when a monitor is placed left of or above the primary display.

from PIL import ImageGrab

image = ImageGrab.grab(all_screens=True)
print('Virtual desktop:', image.size)
image.save('all-monitors.png')

Managed Windows 11 devices can apply App Privacy screenshot policies that allow, deny or leave access under user control for the applicable capture mechanism. Microsoft documents Windows.Graphics.Capture as an API for acquiring frames from a display or application window. These documents do not describe every Python package, so identify the actual backend before changing policy.

3. A repeatable repair workflow

  1. Identify the stack: interpreter, package, version, OS, session type, launch context and traceback.
  2. Run a full-screen capture: omit bbox, print mode and dimensions, and save to a known writable path.
  3. Check display access: inspect Linux display variables and confirm desktop-session access on every OS.
  4. Check native requirements: verify X11 support or documented fallback utilities.
  5. Check policy: on managed Windows, identify the backend and ask the administrator which policy applies.
  6. Fix cropping last: use the actual dimensions, monitor origin and scaling factor.
  7. Separate saving errors: test the output directory independently.

4. Make cropping portable

A bbox is valid only in the coordinate system used by the capture backend. Validate it against the captured image.

from PIL import ImageGrab

image = ImageGrab.grab()
left, top, right, bottom = 100, 100, 900, 700
width, height = image.size
if not (0 <= left < right <= width and 0 <= top < bottom <= height):
    raise ValueError(f'bbox {(left, top, right, bottom)} outside {image.size}')
image.crop((left, top, right, bottom)).save('crop.png')

For multi-monitor layouts, capture the complete virtual desktop and map the target monitor’s origin. For Retina displays, convert logical coordinates to physical pixels once at the boundary. Avoid fixed coordinates for responsive windows; use a native window or element locator when possible.

5. Choose the right capture route

Route Best fit Check
Pillow ImageGrab Small Python desktop utility Backend, dependencies, scaling and monitor behavior
Native OS API Single-platform application capture User selection, consent and packaging
Linux utility fallback Compatible desktop utility available Session and process access
XDG Portal Sandboxed Linux application Library integration and target selection
ScreenshotNeo Website screenshots without desktop setup URL, authentication and output options

Desktop capture and website capture solve different problems. ImageGrab captures the visible desktop. ScreenshotNeo renders a URL, which is useful for documentation, previews, reports and CI jobs without an interactive display.

6. Or skip the browser setup

For a website screenshot, use ScreenshotNeo’s API. See the ScreenshotNeo documentation for options.

cURL

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)
r.raise_for_status()
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}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
require('fs').writeFileSync('shot.webp', Buffer.from(await res.arrayBuffer()));

ScreenshotNeo supports PNG, JPEG, WebP and PDF output; full-page or CSS-element capture; dark mode; device presets and custom viewports; retina scale; waits for selectors, delays or network idle; lazy-image loading; custom CSS and JavaScript; clicks; hidden selectors; blocked ads, trackers, requests and resource types; headers, cookies, user agents and Authorization; timezone and geolocation; transparent backgrounds; resizing; caching with a chosen TTL; signed links; asynchronous jobs with signed webhooks; bulk capture of up to 100 URLs; usage and OpenAPI APIs; and an MCP server with take_screenshot, get_page_info and capture_pdf.

Before capture, it accepts cookie and consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups and chat widgets. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads and cache hits cost nothing. Responses identify the result with X-Page-Verdict and X-Billed.

The Free plan includes 1,000 shots per month with no card. Paid plans start at $5 for 3,000 shots; yearly billing gives two months free. Create a free ScreenshotNeo account.

7. Performance, reliability and cost

  • Full-screen and all-monitor images move more pixels and take longer to encode. Capture only the needed region after validating coordinates.
  • Remote and CI jobs require a display server, session access and native dependencies. A URL API avoids those workstation requirements.
  • Record dimensions, mode, backend version and exceptions. For ScreenshotNeo, inspect verdict and billing headers, use waits for dynamic pages and enable caching for repeated URLs.
  • Local capture has no API charge but requires desktop maintenance. ScreenshotNeo bills only clean shots; failed loads, bot checks, blank pages, timeouts and cache hits are not billed.

8. Troubleshooting common errors

Symptom Cause Fix
ImportError: PIL Wrong interpreter or environment Print sys.executable and install the package there.
Linux returns no image No X11 access, missing support or incompatible fallback Check display variables, session, helper utilities and library integration.
Wayland fails Backend cannot capture the compositor or use a portal Verify backend support or use a compatible native, utility or portal route.
Black image over SSH or in a service No interactive display Run in the desktop session or use a headless website capture service.
macOS crop is shifted Retina physical pixels differ from logical coordinates Inspect image.size and convert coordinates once.
Windows crop misses a monitor Negative virtual-desktop origin or DPI mismatch Use all_screens=True and map the origin.
Managed Windows denial App Privacy policy or backend permission Identify the API and ask the administrator to review its policy.
Valid image cannot be saved Path or filesystem permission Save to a known writable directory.
Website contains popups or is blank Timing, consent UI, bot check or failed load Use waits or custom CSS/JS; inspect ScreenshotNeo verdict headers.

FAQ

Is Python broken if screenshots work elsewhere?

Usually no. Compare the interpreter, package backend, desktop session, dependencies, scaling and policy.

Does installing gnome-screenshot fix every Linux issue?

No. It is only one documented fallback, and session access still matters.

Why is a Mac screenshot twice as large?

Retina capture is documented at 2x by default. Use actual dimensions and request 1x where supported.

Should I disable Windows privacy controls?

No. Identify the backend and have the device administrator review the applicable policy.

Can ScreenshotNeo capture my desktop window?

It captures website URLs. It is intended for web pages when you want to avoid desktop-session and browser setup.