chibisafe icon indicating copy to clipboard operation
chibisafe copied to clipboard

[BUG] R2 Any file bigger than 2GB> shows no progress.

Open rossiscool123 opened this issue 11 months ago • 2 comments

Describe the bug When using R2 for the storage provider anything smaller than 2GB uploads fine, anything greater then this doesn't seem to upload and seems to immediately fails, logs show nothing failed, just a POST request that acknowledges a request was made. Max Size in the admin dashboard is set to "5000000000" bytes (5GB)

To Reproduce Try and upload a file bigger than 2GB and the upload hangs forever but produce no error response. Example image provided. Image Image

Expected behavior File to upload to R2 without any issue

Side note Tested R2 bucket with SFTPGo and I managed 10GB without any issue, so this is not an issue with CF R2 Buckets.

rossiscool123 avatar May 05 '25 17:05 rossiscool123

I got the same issue uploading files to AWS S3, the progress bar does not run, the file does not goes on. Running v6.5.5

ivancarlosti avatar Jul 24 '25 19:07 ivancarlosti

An AI gave to me this answer, maybe it can solve the issue?

Root cause: 32-bit-sized fields stored as strings in the schema

This Prisma schema reveals the most likely reason large (>2GB) uploads to S3 fail or misbehave in chibisafe: file sizes and limits are stored as String fields rather than true 64-bit integers. That choice often forces conversions/parsing at the application layer, where it’s easy to accidentally pass sizes through code paths that truncate to 32-bit integers, trigger overflow at 2,147,483,647 bytes, or mishandle comparisons and progress math.

Key red flags in the schema:

  • files.size is String
  • settings.maxSize is String
  • settings.chunkSize is String
  • users.storageQuota is String
  • settings.usersStorageQuota is String

Storing byte sizes as String means:

  • Anywhere these values are parsed, a mistaken use of 32-bit integer math, bitwise operators, typed arrays, or libraries expecting number may cap or overflow near 2GB.
  • Comparisons can silently become lexicographic if a parse is forgotten, leading to incorrect allow/deny logic.
  • Any progress accounting, limits enforcement, or multipart part-size calculation based on these strings can go wrong at large values.

Additionally, the database is SQLite here, which supports large integers, so there’s no inherent DB limitation preventing BIGINT-like storage for sizes.

How this causes the 2GB ceiling in practice

  • If the upload controller validates “size <= maxSize” using a parsed integer that ends up in 32-bit range, files above ~2.0GB may be rejected as “too big” or get miscounted, causing logic to abort near the boundary.
  • If multipart partSize or total ContentLength is derived from string values and parsed with a path that truncates, the S3 client may attempt a single PUT or misconfigure parts, leading to failure.
  • UI or API progress can stall at 2,147,483,647 bytes if a 32-bit accumulator is used after parsing strings to numbers, causing watchdogs/timeouts or incorrect “stuck” handling.

Recommended fixes

  1. Change schema types from String to integer types for sizes and quotas
    • files.size: store as Int or BigInt (Prisma supports BigInt; for SQLite, store as Int and ensure application uses 64-bit-safe JavaScript Number or BigInt).
    • settings.maxSize: Int/BigInt
    • settings.chunkSize: Int/BigInt
    • users.storageQuota: Int/BigInt
    • settings.usersStorageQuota: Int/BigInt

If BigInt is used: - In TypeScript, prefer BigInt for internal size arithmetic; when interfacing with SDKs or JSON, carefully convert to Number only when values are < Number.MAX_SAFE_INTEGER. - If sticking with Number, ensure values fit within 53-bit precision and never use 32-bit bitwise ops on sizes. 2. Audit all parsing and arithmetic around sizes - Replace parseInt/Number uses with robust parsers that validate range and units; remove any bitwise operations (e.g., |0) on sizes. - Ensure progress counters and byte totals are stored in 64-bit-safe variables; avoid typed arrays that coerce to 32-bit. - Verify comparisons are numeric, not string-based. 3. Ensure proper multipart upload configuration - Use multipart uploads for large files with sensible partSize (e.g., 8–64MiB). - Never buffer entire files in memory; stream parts. - Provide ContentLength where necessary or use streaming helpers that don’t force full buffering. - Avoid computing whole-file MD5 in memory. 4. Validate DB migrations and app invariants - Add a migration converting size fields to integer columns and backfill existing string data safely. - Add application-level validation: sizes and quotas must be non-negative integers; reject non-numeric strings at config ingestion.

Minimal code-level guardrails (server)

  • Normalize and store sizes as numbers/BigInt at config load (e.g., settings.maxSize, settings.chunkSize).
  • When reading files.size, treat it as a number/BigInt throughout validation and upload logic.
  • Add unit tests with sizes around boundaries: 2,147,483,647; 2,147,483,648; 3,000,000,000; 5,000,000,000 to ensure no truncation or overflow.

Why this aligns with the observed 2GB symptom

A 2GB barrier is the telltale sign of a 32-bit integer limit being hit (2,147,483,647), and given that all critical size fields are strings in this schema, the probability of at least one path parsing them into a 32-bit constrained value is high. Converting these to integer types and auditing parsing/arithmetics typically resolves the issue decisively.

ivancarlosti avatar Aug 17 '25 20:08 ivancarlosti