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
- Inspect chrome.storage.local.probeBunnyCreateFail to see the recorded status/ok/url and identify the HTTP failure mode.
- If status is 401, refresh the Screenity token (re-authenticate) and retry; the uploader itself doesn't retry 4xx by design.
- 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).
- Verify network reachability of API_BASE and that the extension host permissions allow the request.
- 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
- Check probeBunnyCreateFail in storage after failures — it records the HTTP status shape for triage.
- Refresh the Screenity token proactively before long recording sessions to avoid 401s.
- Distinguish transient (5xx/429/network) from permanent (4xx) failures; only auto-retry the former.
- Monitor backend /bunny/videos error rates and Bunny library quota usage.
- Ensure extension host_permissions include API_BASE so fetch isn't blocked.
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
- Failed to create scene: ${errorText}
- Failed to create video project
- Failed to initialize uploaders
- No valid media uploaded - both screen and camera mediaId are
- Uploader has already been initialized
AI-assisted analysis of alyssaxuu/screenity@512606387b (2026-09-02).
Data as JSON: /api/errors/230f94f987bbd3e2.
Report an issue: GitHub.