fullstackhero/dotnet-starter-kit · error · ArgumentOutOfRangeException

Actual size must be positive.

Error message

Actual size must be positive.

What it means

MarkAvailable validates that the actual size reported after upload is > 0 and throws ArgumentOutOfRangeException otherwise. A zero/negative actual size means the stored blob is empty or the size was misreported, which would corrupt quota accounting and download semantics.

Solutions

  1. Verify on the client that the uploaded blob size matches before calling complete; re-PUT the file if size is 0.
  2. Confirm the storage key used for the size probe is the same key the client uploaded to.
  3. Block zero-byte files at selection time in the UI.
  4. If the provider reports 0 after a successful PUT, check MinIO/S3 configuration (multipart completion, bucket policy).

Example fix

// before
await putToPresignedUrl(file);
await api.post(`/files/${id}/complete`); // may 500/422: actual size 0
// after
await putToPresignedUrl(file);
if (file.size <= 0) { showError('empty file'); return; }
await api.post(`/files/${id}/complete`);
Defensive patterns

Strategy: validation

Validate before calling

if (!file || file.size <= 0) { showError('empty file'); return; }
await putToPresignedUrl(file);

Prevention

When it happens

Trigger: Completing an upload where the storage object is 0 bytes (client PUT an empty body, partial/multipart upload failed silently), or the provider-reported ETag/size lookup returned 0.

Common situations: Client aborted the PUT halfway leaving an empty object; wrong storage key passed to the stat call so the size read defaults to 0; uploading an empty file that passed (or bypassed) the declared-size check.

Related errors


AI-assisted analysis of fullstackhero/dotnet-starter-kit@3f2959e683 (2026-09-15). Data as JSON: /api/errors/e7cd1ac2c5493e64. Report an issue: GitHub.

Appendix: source

Thrown at src/Modules/Files/Modules.Files/Domain/FileAsset.cs:94

            ScanStatus = ScanStatus.NotScanned,
            UploadDeadline = uploadDeadline,
            CreatedByUserId = createdByUserId,
            CreatedAtUtc = DateTime.UtcNow
        };
    }

    public void MarkAvailable(long actualSize, ScanStatus scanResult)
    {
        if (Status != FileAssetStatus.PendingUpload)
        {
            throw new CustomException(
                $"Cannot finalize file in status {Status}.",
                errors: null,
                HttpStatusCode.Conflict);
        }
        if (actualSize <= 0)
        {
            throw new ArgumentOutOfRangeException(nameof(actualSize), "Actual size must be positive.");
        }

        SizeBytes = actualSize;
        ScanStatus = scanResult;
        Status = scanResult == ScanStatus.Infected ? FileAssetStatus.Quarantined : FileAssetStatus.Available;
        UploadDeadline = null;
        UpdatedAtUtc = DateTime.UtcNow;

        AddDomainEvent(DomainEvent.Create((id, ts) =>
            new FileFinalizedDomainEvent(Id, OwnerType, OwnerId, Status, id, ts)));
    }

    /// <summary>Reverses a soft delete. Idempotent.</summary>
    public void Restore()
    {
        if (!IsDeleted) return;
        IsDeleted = false;
        DeletedOnUtc = null;

View on GitHub (pinned to 3f2959e683)