alyssaxuu/screenity · error · Error

write-after-finalize

write-after-finalize

Error message

Cannot write during finalization

What it means

write() refuses chunks once the uploader has entered finalization (isFinalizing true). Any bytes arriving after finalize begins cannot be delivered, so the chunk size is recorded in bytesLostAfterFinalize, an uploader error is set, and the chunk is rejected.

Source

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

        }),
      }).catch((err) =>
        this.debugLog("save-upload-meta failed (non-blocking)", { error: String(err) }),
      );
    } else {
      this.debugLog("Skipping save-upload-meta because user token is missing", {
        mediaId: this.mediaId,
        uploadUrl: this.uploadUrl,
        signature: this.signature,
        expires: this.expires,
      });
    }
  }

  async write(chunk) {
    if (this.isFinalizing) {
      this.bytesLostAfterFinalize += chunk?.size || 0;
      this.setUploaderError("write-after-finalize");
      throw new Error("Cannot write during finalization");
    }
    if (this.isPaused) throw new Error("Uploader paused");
    if (!this.uploadUrl) throw new Error("Uploader not initialized");

    if (this.status === "error") {
      throw new Error(`Uploader in error state: ${this.error}`);
    }

    await this.checkAuthExpiration();
    this.status = "uploading";
    if (!this.hasEmittedClientStarted) {
      this.hasEmittedClientStarted = true;
      this.emitTelemetry("upload_client_started");
    }

    for (let i = 0; i < chunk.size; i += this.CHUNK_SIZE) {
      const subChunk = chunk.slice(i, i + this.CHUNK_SIZE);
      this.chunkQueue.push(subChunk);

View on GitHub (pinned to 512606387b)

Solutions

  1. Stop the media recorder and wait for its final dataavailable event BEFORE calling finalize
  2. Queue chunks and drain the queue before starting finalization (set isFinalizing only after the queue is empty)
  3. Check uploader.isFinalizing in the write path and drop/flush chunks gracefully instead of crashing callers
  4. Inspect bytesLostAfterFinalize to quantify how much data was dropped and adjust the stop sequence

Example fix

// before
await uploader.finalize();
recorder.stop(); // chunks still arrive after finalize
// after
recorder.stop();
await once(recorder, "stop"); // flush all remaining chunks
await uploader.finalize();
Defensive patterns

Strategy: try-catch

Validate before calling

if (uploader.isFinalizing) {
  lostBytes += chunk.size; // park or drop before calling write
}

Try / catch

try {
  await uploader.write(chunk);
} catch (e) {
  if (String(e.message).includes("Cannot write during finalization")) {
    // stop feeding; finalize is already in progress
    recorderStreamClose();
  }
}

Prevention

When it happens

Trigger: Calling uploader.write(chunk) after finalize()/stop was invoked, typically from a recording callback that is still delivering MediaRecorder/webcodecs data while the stop path is running.

Common situations: Race between the recorder's last dataavailable event and the stop/finalize flow; user stops the recording while a burst of chunks is queued; double-stop wiring firing finalize before the final flush.

Related errors


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