alyssaxuu/screenity · critical · Error

Failed to create Bunny video

Error message

Failed to create Bunny video

What it means

initialize() creates the Bunny video by POSTing to `${API_BASE}/bunny/videos` with the Screenity bearer token. The call is retried up to 3 times for transient failures (network errors, 5xx, 429), but if the final response is missing or not ok, initialize() throws this error after persisting a failure probe (probeBunnyCreateFail) to chrome.storage.local for post-mortem. It means the backend refused or failed to create the video, so no upload target exists.

Source

Thrown at src/pages/CloudRecorder/bunnyTusUploader.js:1002

        });

        if (!res || !res.ok) {
          // PROBE: persist failure shape so post-mortem can see what
          // came back. Removed once Playwright mock race is identified.
          try {
            await chrome.storage.local.set({
              probeBunnyCreateFail: {
                ts: Date.now(),
                resPresent: !!res,
                status: res?.status ?? null,
                type: res?.type ?? null,
                url: res?.url ?? null,
                redirected: res?.redirected ?? null,
                ok: res?.ok ?? null,
              },
            });
          } catch {}
          throw new Error("Failed to create Bunny video");
        }
        const data = await res.json();
        this.videoId = data.videoId;
        this.mediaId = data.mediaId;
        // The server now inlines the TUS upload signature in this
        // response (data.tusAuth). When present, stash it so the
        // immediate-next refreshTusAuth() can skip its GET round-trip.
        // Saves ~300-1000ms per uploader in dev. Falls back to the
        // GET path when tusAuth is missing (older server / resume).
        if (data?.tusAuth?.signature && data.tusAuth.expires && data.tusAuth.libraryId) {
          this._inlinedTusAuth = {
            signature: data.tusAuth.signature,
            expires: data.tusAuth.expires,
            libraryId: data.tusAuth.libraryId,
          };
        }
        await this.persistVideoMap({
          projectId,

View on GitHub (pinned to 512606387b)

Solutions

  1. Inspect chrome.storage.local.probeBunnyCreateFail to see the recorded status/ok/url and identify the HTTP failure mode.
  2. If status is 401, refresh the Screenity token (re-authenticate) and retry; the uploader itself doesn't retry 4xx by design.
  3. If status is 5xx/429 or res is null, wait briefly and retry initialize() — transient failures are only retried 3 times with short backoff (500ms, 1000ms).
  4. Verify network reachability of API_BASE and that the extension host permissions allow the request.
  5. Check backend logs for the POST /bunny/videos handler to find the server-side cause (Bunny credentials, library limits, validation).

Example fix

// before
await uploader.initialize({ projectId, sceneId, type });
// after
try {
  await uploader.initialize({ projectId, sceneId, type });
} catch (err) {
  if (String(err?.message).includes("Failed to create Bunny video")) {
    const probe = (await chrome.storage.local.get("probeBunnyCreateFail")).probeBunnyCreateFail;
    if (probe?.status === 401) await reauthenticateScreenity();
    else await new Promise(r => setTimeout(r, 2000)); // back off for transient
  }
  await uploader.initialize({ projectId, sceneId, type });
}
Defensive patterns

Strategy: retry

Validate before calling

const { authenticated, screenityToken } = await getAuthState();
if (!authenticated || !screenityToken) throw new Error('Sign in before starting a cloud recording');

Try / catch

try {
  await uploader.initialize({ projectId, sceneId, type });
} catch (err) {
  if (String(err?.message).includes('Failed to create Bunny video')) {
    const probe = (await chrome.storage.local.get('probeBunnyCreateFail')).probeBunnyCreateFail;
    const transient = probe == null || probe.status == null || probe.status >= 500 || probe.status === 429;
    if (transient) {
      await sleep(2000);
      return uploader.initialize({ projectId, sceneId, type });
    }
    if (probe?.status === 401) await reauthenticateScreenity();
  }
  throw err;
}

Prevention

When it happens

Trigger: POST /bunny/videos returned a non-ok status (e.g. 400 validation, 401 expired token, 429 rate limit, 5xx after 3 attempts) or all fetch attempts threw network errors, leaving res null or res.ok false on the create-new-video path of initialize().

Common situations: Expired/invalid screenityToken producing 401; Bunny library quota or API key problems surfacing as 4xx/5xx; offline or blocked network (CSP, proxy) so fetch throws every attempt; backend deployment regression; server-side rate limiting during parallel uploads.

Understand the failure class

Background: "API request failed": what wrapped HTTP errors from external APIs mean and how to find the real cause — this error's family across 29 libraries.

Related errors


AI-assisted analysis of alyssaxuu/screenity@512606387b (2026-09-02). Data as JSON: /api/errors/230f94f987bbd3e2. Report an issue: GitHub.