Skip to content

fix: don't take a screenshot at import time (breaks headless imports)#23

Merged
abrichr merged 1 commit into
mainfrom
fix/no-screenshot-at-import
Jun 13, 2026
Merged

fix: don't take a screenshot at import time (breaks headless imports)#23
abrichr merged 1 commit into
mainfrom
fix/no-screenshot-at-import

Conversation

@abrichr

@abrichr abrichr commented Jun 13, 2026

Copy link
Copy Markdown
Member

Summary

recorder.py computed monitor dimensions at module scope via utils.take_screenshot().size. So import openadapt_capture executes a screenshot on import, and crashes in any headless environment whose display reports a zero-size region:

mss.exception.ScreenShotError: Region has zero or negative size: {'left': 0, 'top': 0, 'width': 0, 'height': 0}

That also took down openadapt version and openadapt doctor, which import sibling packages to read their versions — found via the new CLI smoke tests in OpenAdaptAI/OpenAdapt#1003.

Fix: move the dimension computation into record_video's setup, its only consumer (the screenshot now runs when recording starts, not on import).

Regression guard

tests/test_headless_import.py is a deterministic AST check that no package module calls a display API (take_screenshot/get_monitor_dims/grab) at import scope. A subprocess import test is unreliable — it only reproduces on a genuinely headless display — so this asserts the invariant statically instead. Verified: passes on this fix, fails on the pre-fix code.

Verification

  • import openadapt_capture succeeds headless after the fix
  • Full suite unchanged vs main (2 failed, 16 errors are pre-existing in this sandbox and identical with/without this change)

🤖 Generated with Claude Code

recorder.py computed monitor dimensions via utils.take_screenshot() at
module scope, so `import openadapt_capture` crashed in any headless
environment whose display reported a zero-size region. This took down
`openadapt version` and `openadapt doctor` (found via the new CLI smoke
tests in OpenAdaptAI/OpenAdapt). Move the computation into the video-
setup function that is its only consumer.

Adds tests/test_headless_import.py: a deterministic AST guard that no
package module calls a display API (take_screenshot/get_monitor_dims/
grab) at import scope. A subprocess import test is unreliable (only
reproduces on a genuinely headless display); this fails regardless of
environment.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@abrichr abrichr force-pushed the fix/no-screenshot-at-import branch from 5e97386 to 1097148 Compare June 13, 2026 15:57
@abrichr abrichr merged commit d4e2016 into main Jun 13, 2026
4 checks passed
@abrichr abrichr deleted the fix/no-screenshot-at-import branch June 13, 2026 15:58
abrichr added a commit that referenced this pull request Jul 12, 2026
…tio on the recording model (#24)

* fix: tolerate headless screenshot failure on import

The recorder no longer takes a screenshot at module load (PR #23), but
the package import guard only caught ImportError. Broaden it so a
display/screenshot failure (mss ScreenShotError) during recorder import
also degrades to `Recorder = None` instead of crashing the whole
package on a headless host (CI, containers, sandboxes).

Add a runtime headless-import test that imports the package fresh in a
subprocess where mss.grab raises ScreenShotError, complementing the
existing static (AST) guard.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CKrVJJy5jWVCkXAqgUqtqZ

* fix: persist pixel_ratio on the SQLAlchemy recording model

CaptureSession.pixel_ratio read `pixel_ratio` off the SQLAlchemy
Recording model, but that model had no such column (only the legacy
raw-sqlite schema did), so a HiDPI capture whose config JSON lacked it
silently scaled at 1.0, under-scaling downstream coordinate mapping.

Add a nullable `pixel_ratio` column to the Recording model and write the
captured display's ratio (platform.get_display_pixel_ratio()) at record
time in create_recording. Add an additive ALTER TABLE ADD COLUMN
migration (migrate_missing_columns) run when opening any per-capture DB,
so older recording.db files that predate the column still load: the
column is added as NULL, and CaptureSession.pixel_ratio falls back to the
config JSON, then 1.0 when genuinely unknown.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CKrVJJy5jWVCkXAqgUqtqZ

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant