Skip to content

Second browser instance silently renders 2D forever: XR_ERROR_LIMIT_REACHED is retried as transient and never surfaced #162

Description

@dfattal

Symptom

Launch a second DisplayXR Browser instance while one is already running (e.g. a leftover default-profile window plus a test instance) and the second one silently renders everything in 2D forever. No dialog, no infobar, no visible error — indistinguishable from "the weave is broken".

Observed 2026-08-25 on browser 0.1.19 + runtime v2.13.1, during an unrelated hardware verification:

xrCreateInstance with the rig extensions failed: -10 — retrying weave-only (inline-3D scenes stay mono)
xrCreateInstance failed: -10 (is displayxr-service running?)
weave init failed transiently (service restarting?) — retrying (23 attempts left)
weave init failed transiently (service restarting?) — retrying (22 attempts left)

This is a correct refusal, reported as the wrong kind of failure

-10 is XR_ERROR_LIMIT_REACHED. The runtime enforces a per-class client quota (displayxr-runtime#960):

CONTROLLER    1
RELAY         1
PRESENT_OWNER 2      <- the browser's class
DIAG          4
APP           max_clients - 1  (~31)

The browser is a PRESENT_OWNER and needs both slots for a single instance — one for the GPU process (the weaving session) and one for the browser process (display mode, popup occluders, the sticky flat channel). So one browser exhausts the class, and a second instance is correctly refused: a present-owner owns the panel and presents to it directly, and there is exactly one panel.

The refusal is right. The reporting is wrong, in three ways:

  1. It is classified as transient. The log says "weave init failed transiently (service restarting?)" and enters the 24-attempt retry ladder. A class quota is not transient — no amount of retrying frees a slot held by a live process. This is the same shape as the skew case in displayxr-runtime#1200's comment thread: a permanent condition wearing a retryable error's clothes.
  2. The user is told nothing. The one visible consequence is "3D doesn't work", which is exactly what a genuinely broken weave looks like. Cost a round of hardware testing here; a user would have no path at all.
  3. The hint is actively misleading. "(is displayxr-service running?)" points at the one thing that is definitely fine — the service is running and healthy, which is why the slot is taken.

Suggested fix

  • Classify XR_ERROR_LIMIT_REACHED as terminal, not transient. Skip the retry ladder — it cannot succeed.
  • Say what happened, once: "Another DisplayXR Browser instance already has the display. Close it and relaunch to use inline-3D here." An infobar or a start-page banner would do; the existing skew path (patch 0075/0077) already has the machinery for a terminal weave-mode message and this is the same class of condition.
  • Drop the "is the service running?" hint for this code specifically — it is wrong precisely when this fires.

Related, filed separately

The service side could name the offending PID in its refusal so the log says which process holds the slot. That is displayxr-runtime#1207.

Note on scope

Not proposing to raise the quota. Two present-owners on one panel has no coherent meaning — both would be driving one display's lens state and scanout. The limit is right; only its presentation is wrong.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions