Field report (2026-09-01, Windows host, native client, PUNKTFUNK_VDISPLAY_HZ_MULT=2): every stream start crashed immediately; a host restart made no difference. Log shows two fresh sessions dying identically.
Root cause
Two callers in build_pipeline_with_retry disagreed about the virtual display's mode:
The IDD-push retry-hold lease created the monitor at the plain session mode (vd.create(mode) → arrives at 3840x2160@120).
build_pipeline's acquire then asked for display_mode_for(mode) (@240), which the manager satisfied with a live in-place resize on the monitor created milliseconds earlier.
Windows pins a monitor's settable mode set at arrival, so the 240 Hz set was rejected (DISP_CHANGE_BADMODE in the log) — and the IddCxMonitorUpdateModes2 mode-list churn landed under the opening capture ring. From then on the driver dropped every composed frame as a size/format mismatch ("driver attached and DWM composed 142 frame(s), but none matched the ring"), the build burned all 8 retries, and the session ended.
Fix
One line: the retry-hold lease requests the same display_mode_for(mode) the build acquires. The monitor arrives at the multiplied rate — no mid-open churn, and the multiplied refresh is in the arrival mode list so it actually commits. A driver/OS that still refuses it falls back through the existing achieved-rate path (pacing_hz), which already handles a clamped refresh cleanly — it was the churn that killed the session, not the clamp.
Not touched: the GameStream plane never applies the multiplier (consistent within itself), and the Linux registry pool doesn't use the Windows refcount manager, so neither path has this mismatch.
Verification
Traced the field log against the code end to end; the log's sequence (create @120 → in-place resize 120→240 → BADMODE → ring mismatch ×8 → session end) matches the diagnosed path exactly.
Compile: the host crate doesn't build on macOS (target-gated deps) and Windows cross-compile stops at openh264's C build, so CI is the compile gate. Runtime confirmation needs the reporter's host on a build from this branch.
## Symptom
Field report (2026-09-01, Windows host, native client, `PUNKTFUNK_VDISPLAY_HZ_MULT=2`): every stream start crashed immediately; a host restart made no difference. Log shows two fresh sessions dying identically.
## Root cause
Two callers in `build_pipeline_with_retry` disagreed about the virtual display's mode:
1. The IDD-push **retry-hold lease** created the monitor at the plain session mode (`vd.create(mode)` → arrives at `3840x2160@120`).
2. `build_pipeline`'s acquire then asked for `display_mode_for(mode)` (`@240`), which the manager satisfied with a live **in-place resize** on the monitor created milliseconds earlier.
Windows pins a monitor's settable mode set at arrival, so the 240 Hz set was rejected (`DISP_CHANGE_BADMODE` in the log) — and the `IddCxMonitorUpdateModes2` mode-list churn landed under the opening capture ring. From then on the driver dropped every composed frame as a size/format mismatch ("driver attached and DWM composed 142 frame(s), but none matched the ring"), the build burned all 8 retries, and the session ended.
## Fix
One line: the retry-hold lease requests the same `display_mode_for(mode)` the build acquires. The monitor arrives at the multiplied rate — no mid-open churn, and the multiplied refresh is in the arrival mode list so it actually commits. A driver/OS that still refuses it falls back through the existing achieved-rate path (`pacing_hz`), which already handles a clamped refresh cleanly — it was the churn that killed the session, not the clamp.
Not touched: the GameStream plane never applies the multiplier (consistent within itself), and the Linux registry pool doesn't use the Windows refcount manager, so neither path has this mismatch.
## Verification
- Traced the field log against the code end to end; the log's sequence (create @120 → in-place resize 120→240 → BADMODE → ring mismatch ×8 → session end) matches the diagnosed path exactly.
- `WRITING_BASE=origin/main scripts/ci/check-writing.py` passes.
- Compile: the host crate doesn't build on macOS (target-gated deps) and Windows cross-compile stops at openh264's C build, so CI is the compile gate. Runtime confirmation needs the reporter's host on a build from this branch.
With PUNKTFUNK_VDISPLAY_HZ_MULT set, every Windows IDD-push session died
at bring-up: the retry-hold lease created the monitor at the session
rate, then the build's acquire asked for the multiplied rate, forcing an
in-place mode churn on the just-arrived monitor. The OS pins settable
modes at arrival, so the multiplied rate was BADMODE-rejected — and the
mode-list churn landed under the opening capture ring, whose frames then
all dropped as size/format mismatches until the retries ran out.
Make the lease request the same display_mode_for(mode) the build
acquires. The monitor arrives at the multiplied rate — no mid-open
churn, and the rate is in the arrival list so it actually commits. A
driver that still cannot run it falls back through the existing
achieved-rate path.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Symptom
Field report (2026-09-01, Windows host, native client,
PUNKTFUNK_VDISPLAY_HZ_MULT=2): every stream start crashed immediately; a host restart made no difference. Log shows two fresh sessions dying identically.Root cause
Two callers in
build_pipeline_with_retrydisagreed about the virtual display's mode:vd.create(mode)→ arrives at3840x2160@120).build_pipeline's acquire then asked fordisplay_mode_for(mode)(@240), which the manager satisfied with a live in-place resize on the monitor created milliseconds earlier.Windows pins a monitor's settable mode set at arrival, so the 240 Hz set was rejected (
DISP_CHANGE_BADMODEin the log) — and theIddCxMonitorUpdateModes2mode-list churn landed under the opening capture ring. From then on the driver dropped every composed frame as a size/format mismatch ("driver attached and DWM composed 142 frame(s), but none matched the ring"), the build burned all 8 retries, and the session ended.Fix
One line: the retry-hold lease requests the same
display_mode_for(mode)the build acquires. The monitor arrives at the multiplied rate — no mid-open churn, and the multiplied refresh is in the arrival mode list so it actually commits. A driver/OS that still refuses it falls back through the existing achieved-rate path (pacing_hz), which already handles a clamped refresh cleanly — it was the churn that killed the session, not the clamp.Not touched: the GameStream plane never applies the multiplier (consistent within itself), and the Linux registry pool doesn't use the Windows refcount manager, so neither path has this mismatch.
Verification
WRITING_BASE=origin/main scripts/ci/check-writing.pypasses.