{"record":{"id":"6e32f2490a2bc43e","repo":"alyssaxuu/screenity","slug":"resume-offset-unverified","errorCode":"resume-offset-unverified","errorMessage":"Could not verify server offset during resume.","messagePattern":"Could not verify server offset during resume\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/pages/CloudRecorder/bunnyTusUploader.js","lineNumber":1082,"sourceCode":"        }\n        throw refreshErr;\n      }\n\n      if (this.uploadUrl) {\n        const serverOffset = await this.getServerOffset();\n        if (Number.isFinite(serverOffset) && serverOffset >= 0) {\n          this.lastServerOffset = serverOffset;\n          this.offset = serverOffset;\n          this.totalBytes = Math.max(this.totalBytes || 0, serverOffset);\n          if (this.initializedFromResume || serverOffset > 0) {\n            this.emitTelemetry(\"upload_resumed\", {\n              resumedOffset: serverOffset,\n              resumeCount: this.resumeCount || 1,\n            });\n          }\n        } else {\n          this.setUploaderError(\"resume-offset-unverified\");\n          throw new Error(\"Could not verify server offset during resume.\");\n        }\n      } else {\n        await this.initTusUpload();\n      }\n\n      await this.persistUploadJournal({ force: true });\n      this.startHeartbeat();\n      this.status = \"ready\";\n      this.readyAt = Date.now();\n      this.emitTelemetry(\"upload_started\", {\n        resumed: this.initializedFromResume,\n        resumeCount: this.resumeCount || 0,\n      });\n      this.scheduleJournalPersist({ force: true });\n      return { videoId: this.videoId, mediaId: this.mediaId };\n    } catch (err) {\n      if (this.status !== \"error\") {\n        this.setUploaderError(\"initialize-failed\", err);","sourceCodeStart":1064,"sourceCodeEnd":1100,"githubUrl":"https://github.com/alyssaxuu/screenity/blob/512606387b8d07dda5e63bb428bd063f0a2a3ed0/src/pages/CloudRecorder/bunnyTusUploader.js#L1064-L1100","documentation":"When initialize() resumes from a persisted journal with a saved uploadUrl, it queries the TUS server for the current byte offset via getServerOffset() to avoid re-uploading or corrupting data. If the response is not a finite non-negative number, the uploader cannot trust the resume position, marks the uploader error as 'resume-offset-unverified', and throws rather than guessing an offset. This is a safety guard: resuming at a wrong offset would produce a corrupted Bunny video.","triggerScenarios":"initialize() loaded a resume journal that had an uploadUrl, then getServerOffset() returned NaN/null/undefined or a negative value — e.g. the TUS HEAD request failed, returned an unexpected status, or the Upload-Offset header was missing/unparseable.","commonSituations":"The Bunny video was deleted server-side (orphan reaper / manual cleanup) so the TUS URL 404s; upload URL expired or signature invalidated between sessions; network error during the offset HEAD request; server response shape changed so the offset header isn't parsed.","solutions":["Clear the stale upload journal (uploader.clearUploadJournal()) and call initialize() again so it starts a fresh TUS upload instead of resuming.","Check whether the TUS upload URL still exists (HEAD it manually); a 404 means the video was deleted and resume is impossible — create a new video.","Inspect getServerOffset() response handling: verify the request succeeded and the Upload-Offset header is present; fix parsing if the server changed headers.","If it was a transient network failure during the HEAD, simply retry initialize() — the journal is still intact.","If this recurs, check tus-auth signature expiry: an expired signature can make the offset request fail; ensure refreshTusAuth() succeeds before offset verification."],"exampleFix":"// before\nawait uploader.initialize({ projectId, sceneId, type });\n// after\ntry {\n  await uploader.initialize({ projectId, sceneId, type });\n} catch (err) {\n  if (err?.message?.includes(\"verify server offset\")) {\n    await uploader.clearUploadJournal(); // discard untrusted resume state\n    await uploader.initialize({ projectId, sceneId, type }); // fresh upload\n  } else throw err;\n}","handlingStrategy":"try-catch","validationCode":"const res = await fetch(uploadUrl, { method: 'HEAD' });\nconst offset = Number(res.headers.get('Upload-Offset'));\nif (res.ok && Number.isFinite(offset) && offset >= 0) {\n  await uploader.initialize({ projectId, sceneId, type }); // safe to resume\n}","typeGuard":"function isVerifiableOffset(v) {\n  return typeof v === 'number' && Number.isFinite(v) && v >= 0;\n}","tryCatchPattern":"try {\n  await uploader.initialize({ projectId, sceneId, type });\n} catch (err) {\n  if (err?.message?.includes('verify server offset') || err?.code === 'resume-offset-unverified') {\n    await uploader.clearUploadJournal(); // drop untrusted resume state\n    await uploader.initialize({ projectId, sceneId, type }); // fresh upload\n  } else throw err;\n}","preventionTips":["Clear the journal when the Bunny video is known deleted (tus-auth 404) instead of retrying resume forever.","HEAD the uploadUrl yourself before resuming a long-interrupted upload.","Treat signature expiry: ensure tus-auth refresh succeeds before offset verification.","Persist a last-verified timestamp with the journal and skip resume if it's too old.","Never bypass the offset check — resuming at a wrong offset corrupts the video."],"tags":["resume","upload","tus","offset","integrity"],"backgroundTag":"upload-resume-failed","analyzedSha":"512606387b8d07dda5e63bb428bd063f0a2a3ed0","analyzedAt":"2026-09-02T20:59:09.419Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}