#2275·Cap

Self-hosted + Cloudflare R2: camera-only Instant recordings fail with "InvalidPart: All non-trailing parts must have the same length"

Author: evanshieldsCreated Sep 11, 2026Updated Sep 11, 2026

Summary

On a self-hosted instance that uses Cloudflare R2 as the S3 bucket, camera-only Instant Mode recordings from Cap Desktop (Windows) never finish uploading. Screen, window and area Instant recordings upload and play fine against the same bucket.

cap-web logs this on each camera-only attempt:

Multipart upload failed: eS [StorageError]: { "cause": S3Error: { "cause": InvalidPart: All non-trailing parts must have the same length. } (cause: InvalidPart: All non-trailing parts must have the same length.) }

The video row is created with source: {"type": "desktopMP4"}, but no outputKey, previewKey or thumbnailKey is ever set.

Likely cause

Cloudflare R2's multipart API has a rule AWS S3 doesn't: every part except the last must be exactly the same size (R2 docs: "All parts except the last one must be the same size"). The progressive uploader appears to send variable-size parts: flushes of at least MIN_PART_SIZE_BYTES (5 MiB), sized by whatever was buffered, as in packages/recorder-core/src/instant-mp4-uploader.ts. AWS and MinIO accept that; R2 rejects it at CompleteMultipartUpload. Camera-only seems to take a different upload path from screen recordings, so only that mode hits it.

Environment

  • Self-hosted via Docker Compose, ghcr.io/capsoftware/cap-web@sha256:8ee4cbd3fd87... (latest as of 2026-09-07) and cap-media-server@sha256:886ecc9b5684...
  • Storage: Cloudflare R2, CAP_AWS_REGION=auto, path-style, private bucket
  • Cap Desktop for Windows (current download as of 2026-09-11; bundled CLI 0.1.0)

Steps to reproduce

  1. Point Cap Desktop at a self-hosted server whose bucket is Cloudflare R2.
  2. Instant Mode, choose camera only (no screen), record about 30 s, stop.
  3. The upload never completes; cap-web logs the InvalidPart error above.
  4. Repeat with a screen or window target: it works.

Possible fixes

  • Use fixed-size parts for all non-final parts (re-chunk the buffer to a constant size, e.g. 5 or 8 MiB, carrying the remainder forward), or
  • Detect R2 endpoints (or add a S3_FIXED_PART_SIZE option) and switch to constant part sizes there.

Workaround for now: record camera-only videos in Studio Mode.