Skip to content

Return client errors and restore failed display changes - #445

Open
hiroTamada wants to merge 1 commit into
mainfrom
hypeship/fix-display-resize
Open

hiroTamada wants to merge 1 commit into
mainfrom
hypeship/fix-display-resize

Conversation

@hiroTamada

@hiroTamada hiroTamada commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Summary

A rejected non-preset viewport change currently becomes a 500 and can leave the X root at the dummy driver's maximum resolution after mode creation.

  • Preserve Neko screen-change error status and map its 4xx responses to 400 from PATCH /display and both live/restart POST /configure paths. Other failures remain 500.
  • Capture the previous Xorg resolution and actual refresh rate, then restore them after a failed screen change. Restoration uses the existing convergence/retry logic because Neko can accept a reconfiguration without applying it, and uses a bounded context independent of request cancellation.
  • Read the active starred refresh rate from xrandr rather than inferring it from a mode-name suffix; return the realized integer rate after a successful headful resize.
  • Add regression coverage for error mapping, rollback, dropped restoration requests, and refresh-rate parsing.

Testing

  • cd server && go test -race $(go list ./... | grep -v '/e2e$') — passed on the final full run. An initial run failed the existing Chromium restart test during temporary-directory cleanup; its isolated rerun and subsequent full runs passed.
  • cd server && go vet ./... — passed.
  • Built the headful Docker image and the API binary locally.
  • Reproduced the original behavior using the unmodified API/Neko binaries: 379x480@60 returns 500 and leaves X at 3840x2160@25.
  • Verified this change with the existing Neko binary: 379x480@60 returns 400 and restores the previous 1920x1080@25 display. Regression tests also cover a dropped first restoration request.
  • With the companion Neko fix built locally, PATCH /display and POST /configure both pass this matrix, checked with DISPLAY=:1 xrandr --current:
    • 379x480 with no rate after establishing a 60 Hz mode: success at 376x480@59 (59.21 Hz timings).
    • 379x480@59 and repeated 379x480@60: success at 376x480@59.
    • 1280x800@60: success (59.81 Hz timings, integer rate 60).
    • 1920x1080@25: success.
    • 1920x1080@120: 400, display unchanged. Also checked the configure restart path.
  • Verified advancing live-view video and connected WebRTC ICE.
  • The full repository E2E suite, control-plane/SDK end-to-end behavior, and arm64 runtime were not exercised.

Follow-up

The companion rate-selection fix in kernel/neko#24 needs a release and a base-image bump to take effect in released browser images. This PR deliberately leaves ghcr.io/kernel/neko/base:3.0.8-v1.6.0 unchanged; error mapping and rollback work with that existing base. Control-plane default refresh-rate logic is unchanged. Nothing is merged or deployed.


Note

Medium Risk
Touches headful display resize, Neko integration, and rollback on failure—user-visible resolution and API status codes—but behavior is narrowed (4xx mapping, restore on error) with new regression tests.

Overview
Rejected viewport changes no longer surface as 500s when Neko returns a client error, and a failed resize no longer leaves the X root stuck at the dummy driver’s max resolution.

Neko screen-configuration failures now carry their HTTP status (ScreenConfigurationError). PATCH /display and Chromium configure (live and stop/start display steps) treat Neko 4xx as 400; other failures stay 500.

Before applying a new mode, the API records the current Xorg size and refresh rate from xrandr. If the change fails (including after mode creation moved the root), it restores the prior configuration with the same converge/retry path used for resizes, on a bounded context that survives request cancellation.

Refresh-rate handling now parses the active starred rate from xrandr --current (not the mode-name suffix) and returns the realized integer rate after a successful headful resize. Regression tests cover xrandr parsing, 400 vs 500 mapping, rollback (including a dropped restoration request on the live configure path).

Reviewed by Cursor Bugbot for commit 3e5f29b. Bugbot is set up for automated code reviews on this repo. Configure here.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 3e5f29b. Configure here.

defer cancel()
if _, _, restoreErr := s.setResolutionAndConverge(restoreCtx, previousW, previousH, previousRate, useNeko); restoreErr != nil {
logger.FromContext(ctx).Error("failed to restore display after resize failure", "error", restoreErr)
err = fmt.Errorf("%w; failed to restore display: %s", err, restoreErr)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Restore failure still returns 400

Medium Severity

A failed rollback still wraps the original ScreenConfigurationError with %w, so isNekoScreenRejection maps PATCH /display and configure apply to 400 even when restore did not bring the X root back. Callers treat that as a rejected request with an unchanged display, while the screen can remain at the dummy driver's maximum.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 3e5f29b. Configure here.

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