alyssaxuu/screenity · error · Error
Missing user token for saving upload metadata
Error message
Missing user token for saving upload metadata
What it means
initialize() reports the user as authenticated via check-auth-status, but the actual bearer token used to authorize the POST /bunny/videos call is read from chrome.storage.local as `screenityToken`. If that key is absent or empty, there is no credential to attach to the request, so initialize() throws before any network call — it can't save upload metadata or create the Bunny video without it. This catches the mismatch where the background says 'authenticated' but the page-local token copy is missing.
Source
Thrown at src/pages/CloudRecorder/bunnyTusUploader.js:933
});
}
}
if (!this.videoId || !this.mediaId) {
const { authenticated, user } = await new Promise((resolve) => {
chrome.runtime.sendMessage({ type: "check-auth-status" }, resolve);
});
if (!authenticated) throw new Error("Not authenticated with Screenity");
const { screenityToken } = await chrome.storage.local.get([
"screenityToken",
]);
this.userToken = screenityToken;
if (!this.userToken) {
throw new Error("Missing user token for saving upload metadata");
}
// Retry transient failures so a backend blip doesn't abort the
// recording. A 4xx is a real rejection, so don't retry it.
let res = null;
// This POST gates capture start (canBeginRecording waits on uploader
// init), so its server time is start latency.
const endCreatePost = perfSpan("Uploader POST /bunny/videos", { type });
// Also kept on the instance: the perf timeline is capped and evicts
// the start phase on long recordings, so CloudRecorder mirrors these
// into the start-flow trace, which is a single uncapped object.
const createPostStartedAt = Date.now();
let postAttempts = 0;
for (let attempt = 0; attempt < 3; attempt += 1) {
if (attempt > 0) {
await new Promise((r) => setTimeout(r, 500 * attempt));
}
postAttempts = attempt + 1;View on GitHub (pinned to 512606387b)
Solutions
- Re-run the sign-in flow so screenityToken is (re)written to chrome.storage.local, then retry initialize().
- Check chrome.storage.local.get(null) in the same context to confirm the key name is exactly 'screenityToken' and hasn't been renamed or nested.
- Fix the background so it persists the token to storage.local after check-auth-status succeeds (or have check-auth-status return the token directly) so page and background agree.
- Clear the extension's auth state and sign in again if a stale 'authenticated' flag is masking missing storage data.
Example fix
// before
const { screenityToken } = await chrome.storage.local.get(["screenityToken"]);
// after
let { screenityToken } = await chrome.storage.local.get(["screenityToken"]);
if (!screenityToken) {
({ screenityToken } = await chrome.runtime.sendMessage({ type: "get-auth-token" }));
if (screenityToken) await chrome.storage.local.set({ screenityToken });
} Defensive patterns
Strategy: validation
Validate before calling
const { screenityToken } = await chrome.storage.local.get(['screenityToken']);
if (!screenityToken) {
await reauthenticate(); // rewrite screenityToken to storage.local before init
} Type guard
function hasUserToken(state) {
return typeof state?.screenityToken === 'string' && state.screenityToken.length > 0;
} Try / catch
try {
await uploader.initialize({ projectId, sceneId, type });
} catch (err) {
if (String(err?.message).includes('Missing user token')) {
await reauthenticateScreenity(); // restores screenityToken in storage
await uploader.initialize({ projectId, sceneId, type });
} else throw err;
} Prevention
- After any successful sign-in, verify screenityToken exists in chrome.storage.local before enabling recording.
- Keep the storage key name in a shared constant to avoid rename drift between background and page.
- Have check-auth-status also confirm the token is persisted, not just session state.
- Audit storage-clearing code paths (updates, logout) to ensure they re-populate the token on next sign-in.
When it happens
Trigger: initialize() took the create-new-video path (no reuse/journal/video-map hit), check-auth-status returned authenticated:true, but chrome.storage.local.get('screenityToken') resolved undefined/empty — e.g. token was cleared, storage partition mismatch, or the background stores auth elsewhere.
Common situations: chrome.storage.local was cleared (extension update, chrome.identity sync, user cleared site data) while background in-memory auth state still says signed in; a version change renamed the storage key; content script running in a context where storage.local isn't shared with where the token was written.
Related errors
AI-assisted analysis of alyssaxuu/screenity@512606387b (2026-09-02).
Data as JSON: /api/errors/a6a462fc4a9401c6.
Report an issue: GitHub.