Skip to content

feat: skip upload for zero-byte files and publish creation event - #422

Merged
egalvis27 merged 13 commits into
mainfrom
fix/upload-empty-files
Jul 30, 2026
Merged

feat: skip upload for zero-byte files and publish creation event#422
egalvis27 merged 13 commits into
mainfrom
fix/upload-empty-files

Conversation

@egalvis27

@egalvis27 egalvis27 commented Jul 23, 2026

Copy link
Copy Markdown

What is Changed / Added


  • Added a zero-byte file fast path in TemporalFileUploader.
  • If temporal file size is 0, upload is skipped.
  • The flow now publishes the upload domain event directly with an empty ContentsId ('' as ContentsId).
  • Added debug log for skipped empty-file upload.
  • Added helper method isZeroSizeFile(...) for explicit guard logic.
  • Added unit test coverage for empty files:
    • Confirms no watcher is started.
    • Confirms no stream is opened.
    • Confirms uploader factory is not used.
    • Confirms event is still published.

Why

  • Empty files should be created remotely without binary upload.
  • Current upload SDK rejects fileSize: 0 with InvalidUploadSizeError.
  • Skipping upload for zero-byte files prevents this error chain and keeps behavior aligned with product expectation:
    • file is created
    • upload is omitted
    • resulting file has no contentsId.

Summary by CodeRabbit

  • New Features

    • Empty files are handled without attempting an upload and are recorded with zero-byte metadata.
    • Uploads now verify available storage before proceeding.
    • Replacement upload failures provide clearer issue notifications, including the affected filename.
  • Bug Fixes

    • Improved error handling distinguishes empty files from oversized files, rate limits, server errors, and other upload failures.
    • Override failures now preserve more accurate error details for display and troubleshooting.

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@egalvis27, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 51 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ce328b0b-9d41-4c34-a1fb-26ba4b4f6cd8

📥 Commits

Reviewing files that changed from the base of the PR and between b00ad9e and c0e9ae8.

📒 Files selected for processing (8)
  • packages/fuse-daemon/internal/filesystem/setup_test.go
  • src/apps/renderer/messages/virtual-drive-error.ts
  • src/context/virtual-drive/files/application/override/FileOverrider.ts
  • src/context/virtual-drive/files/infrastructure/SDKRemoteFileSystem.test.ts
  • src/context/virtual-drive/files/infrastructure/SDKRemoteFileSystem.ts
  • src/infra/drive-server/drive-server.error.test.ts
  • src/infra/drive-server/drive-server.error.ts
  • src/shared/issues/SyncErrorCause.ts
📝 Walkthrough

Walkthrough

Zero-byte temporal files now bypass upload operations and publish an empty-content event. Drive-server error mapping recognizes empty-file responses, propagating them through persistence, overrides, and replacement notifications. FUSE release tests now use deterministic asynchronous synchronization.

Changes

Empty-file upload handling

Layer / File(s) Summary
Empty-file upload path
src/context/storage/TemporalFiles/domain/*, src/context/storage/TemporalFiles/application/upload/*
TemporalFile.isEmpty() identifies zero-byte files; the uploader publishes an empty-content event, skips validation and file operations, and avoids thumbnail reads.
Empty-file server error mapping
src/infra/drive-server/*, src/context/virtual-drive/files/infrastructure/*, src/context/virtual-drive/files/application/override/*
HTTP error messages can map to EMPTY_FILE or size-related causes, which are converted into desktop errors by persistence and override flows.
Replacement upload notification
src/context/virtual-drive/files/application/create/*, src/apps/drive/dependency-injection/virtual-drive/registerFilesServices.ts
Replacement upload failures notify SyncFileMessenger with the upload error, cause, and file basename.
FUSE release test coordination
packages/fuse-daemon/internal/filesystem/file_test.go
Release tests replace sleep-based coordination with buffered-channel synchronization and explicitly configure the HTTP 500 error handler.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant TemporalFileUploader
  participant eventBus
  participant driveServerClient
  participant CreateFileOnTemporalFileUploaded
  participant SyncFileMessenger
  TemporalFileUploader->>TemporalFileUploader: detect zero-byte file
  TemporalFileUploader->>eventBus: publish empty-content event
  driveServerClient->>driveServerClient: map status and message
  driveServerClient-->>CreateFileOnTemporalFileUploaded: propagate EMPTY_FILE error
  CreateFileOnTemporalFileUploaded->>SyncFileMessenger: report replacement upload issue
Loading

Suggested reviewers: alexismora

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: zero-byte uploads are skipped and a creation event is still published.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/upload-empty-files

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/context/storage/TemporalFiles/application/upload/TemporalFileUploader.test.ts`:
- Around line 30-35: Update the zero-byte upload test using an image extension
so thumbnail-capable handling is exercised, assert that repository.read is not
called, and inspect the published event to verify the aggregate/content ID is
empty while size remains 0 and the path is preserved.

In
`@src/context/storage/TemporalFiles/application/upload/TemporalFileUploader.ts`:
- Around line 35-41: Update TemporalFileUploader’s zero-size branch to publish
the empty-file event without invoking thumbnail extraction or repository.read;
preserve EMPTY_CONTENTS_ID and expected event metadata. In
src/context/storage/TemporalFiles/application/upload/TemporalFileUploader.test.ts
lines 30-35, use an extension that supports thumbnails for the empty file. In
lines 123-131, assert repository.read is not called and verify the published
event contains EMPTY_CONTENTS_ID and the expected metadata.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: dfacca72-0075-4851-b2ff-d4c2a056c3d0

📥 Commits

Reviewing files that changed from the base of the PR and between 46733f0 and 02c2827.

📒 Files selected for processing (2)
  • src/context/storage/TemporalFiles/application/upload/TemporalFileUploader.test.ts
  • src/context/storage/TemporalFiles/application/upload/TemporalFileUploader.ts

Comment thread src/context/storage/TemporalFiles/application/upload/TemporalFileUploader.ts Outdated
@AlexisMora

Copy link
Copy Markdown

You are still not handling the possible 402 caused by POST /files or PUT /files/{uuid}. check drive-server.client.ts where we map the error. on mapStatysToErrorCause we are mapping it always to "file too big" but in reality, we have to check the error message to differenciate between file too big errors, if the user cant upload empty files and got rejected by the server, or if the user has exceeded already the number of empty files that can upload.

Then, for the creation of files i would try and find a way of pushing an issue exactly stating the error that has happened, probably on FileCreator But i dont know, thats up to your criteria where to implement such thing

For the updating of files it should be pretty much the same, probably i would do it on CreateFileOnTemporalFileUploaded.ts.on() probably because here is where we are already checking for the files size rejection.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
src/context/virtual-drive/files/application/create/CreateFileOnOfflineFileUploaded.test.ts (1)

102-120: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Assert the notification filename.

The production change adds name: basename(event.path), but this test only verifies error and cause. Assert the expected basename as well so path-handling regressions are detected.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/context/virtual-drive/files/application/create/CreateFileOnOfflineFileUploaded.test.ts`
around lines 102 - 120, Update the notifier.issues assertion in the
override-rejection test to also verify that the notification’s name equals the
basename of uploadedEvent.path, while preserving the existing error and cause
assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/context/virtual-drive/files/application/create/CreateFileOnTemporalFileUploaded.ts`:
- Around line 61-70: Protect the awaited notifier.issues call in the on() error
path so notifier failures are caught and logged, while processing continues with
the original upload error. Preserve the existing FILE_TOO_BIG handling and
logging behavior after notification attempts.

In `@src/context/virtual-drive/files/infrastructure/SDKRemoteFileSystem.test.ts`:
- Around line 72-82: Run Prettier on the test block in
SDKRemoteFileSystem.test.ts and retain the formatter’s output, including
formatting the long createFileMock.mockResolvedValue error object and assertions
according to the project’s style.

In `@src/infra/drive-server/drive-server.error.ts`:
- Around line 26-45: The mapStatusToErrorCause function must distinguish all
HTTP 402 categories instead of treating every unknown or empty-file response as
FILE_TOO_BIG. Match the backend’s exact size, empty-file, and empty-file-limit
message categories, return distinct existing causes where available, and use a
safe non-size-specific fallback for unknown or missing-message 402 responses;
add regression tests covering each category and fallback.

---

Nitpick comments:
In
`@src/context/virtual-drive/files/application/create/CreateFileOnOfflineFileUploaded.test.ts`:
- Around line 102-120: Update the notifier.issues assertion in the
override-rejection test to also verify that the notification’s name equals the
basename of uploadedEvent.path, while preserving the existing error and cause
assertions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 28a3b42f-840f-4454-a622-ea24d677a93c

📥 Commits

Reviewing files that changed from the base of the PR and between 02c2827 and 3c55dc9.

📒 Files selected for processing (12)
  • src/apps/drive/dependency-injection/virtual-drive/registerFilesServices.ts
  • src/context/storage/TemporalFiles/application/upload/TemporalFileUploader.test.ts
  • src/context/storage/TemporalFiles/application/upload/TemporalFileUploader.ts
  • src/context/virtual-drive/files/application/create/CreateFileOnOfflineFileUploaded.test.ts
  • src/context/virtual-drive/files/application/create/CreateFileOnTemporalFileUploaded.ts
  • src/context/virtual-drive/files/application/override/FileOverrider.ts
  • src/context/virtual-drive/files/infrastructure/SDKRemoteFileSystem.test.ts
  • src/context/virtual-drive/files/infrastructure/SDKRemoteFileSystem.ts
  • src/infra/drive-server/drive-server.client.ts
  • src/infra/drive-server/drive-server.error.test.ts
  • src/infra/drive-server/drive-server.error.ts
  • vitest.setup.main.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/context/storage/TemporalFiles/application/upload/TemporalFileUploader.test.ts
  • src/context/storage/TemporalFiles/application/upload/TemporalFileUploader.ts

Comment thread src/context/virtual-drive/files/infrastructure/SDKRemoteFileSystem.test.ts Outdated
Comment thread src/infra/drive-server/drive-server.error.ts
Comment thread src/context/storage/TemporalFiles/application/upload/TemporalFileUploader.ts Outdated
c.get(FileOverrider),
c.get(Environment),
user.bucket,
c.get(SyncFileMessenger),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking: We could get rid of this class and just use functions as this class has no internal state therefore we could remove one more class that uses diod and this legacy way of importing classes. We could generate a task for it and add a todo inside MainProcessSyncFileMessenger with the ticket name attached to it so that it can be done in a separate class, what do you think?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once we finish this compatibility release, we can get back to addressing the project's technical debt and create tickets to minimize our dependency on diod.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know, i was just saying that you could generate a jira ticket, and add a comment that references said jira ticket so that it does not get forgotten

if (message) {
const normalizedMessage = message.toLowerCase();

if (normalizedMessage.includes('empty file') || normalizedMessage.includes('empty files')) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

on drive server wip
You can not have empty files, upgrade your plan to get more features -> is a 402

      throw new PaymentRequiredException(
        'You can not have empty files, upgrade your plan to get more features',
      );

You can not have more empty files -> is a bad request 400

    if (limit.shouldLimitBeEnforced({ currentCount: emptyFilesCount })) {
      throw new BadRequestException('You can not have more empty files');
    }

and it should be handled accordingly.
As well for the file size limit exceeded, check the error in drive-server-wip

      throw new PaymentRequiredException(
        'File size exceeds the maximum allowed by your plan',
        PaymentRequiredErrorCode.FileUploadSizeExceeded,
      );

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

if (status === 402) return 'FILE_TOO_BIG';
if (status === 400) {
if (isEmptyFileMessage(message)) return 'EMPTY_FILE';
if (isFileSizeLimitMessage(message)) return 'FILE_TOO_BIG';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why a 400? Havent found a case on the drive-server-wip that maps a file size limit exceeded error to a 400

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, I misinterpreted the comment you left me earlier, and when I checked the map, I got confused. I've left only the FILE_EMPTY error, which covers the error indicating that the limit for empty files has been reached.

Comment on lines +44 to +47
normalizedMessage.includes('too big') ||
normalizedMessage.includes('too large') ||
normalizedMessage.includes('file size exceeds') ||
normalizedMessage.includes('size exceeds the maximum allowed')

@AlexisMora AlexisMora Jul 30, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already know that the backend maps these errors: File size exceeds the maximum allowed by your plan for a file that exceeds the file size, so I would delete the 2 first expressions
Check this

 if (Number(fileSize) > Number(limitValue)) {
      throw new PaymentRequiredException(
        'File size exceeds the maximum allowed by your plan',
        PaymentRequiredErrorCode.FileUploadSizeExceeded,
      );
    }

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


const normalizedMessage = message.toLowerCase();

return normalizedMessage.includes('empty file') || normalizedMessage.includes('empty files');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to make a distintion here not only that the error is an empty file but also which type of empty file error we got since the backend already does that for us
402 -> 'You can not have empty files, upgrade your plan to get more features'
400 -> 'You can not have more empty files'
this way we can show different issues

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/infra/drive-server/drive-server.error.test.ts (1)

4-5: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Preserve the distinction between empty-file rejection causes.

Generic empty-file rejection and exceeding the permitted number of empty files currently both map to EMPTY_FILE. Introduce a distinct quota-related cause (or preserve a structured backend reason) so file creation and override callers can report the correct user-facing error.

Also applies to: 24-27

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/infra/drive-server/drive-server.error.test.ts` around lines 4 - 5, Update
mapStatusToErrorCause and its callers so generic empty-file rejection remains
distinct from exceeding the permitted empty-file quota; introduce or preserve a
structured quota-specific cause for the “You can not have more empty files”
response, and ensure file creation and override flows use it for the correct
user-facing error.
🧹 Nitpick comments (1)
src/context/storage/TemporalFiles/application/upload/TemporalFileUploader.test.ts (1)

105-119: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Cover failed space validation.

The test covers hasSpace: false, but not { error: ... }, which maps to BAD_RESPONSE in TemporalFileUploader. Add assertions for the error cause/message and that watch, stream, uploader, and event publication remain untouched.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/context/storage/TemporalFiles/application/upload/TemporalFileUploader.test.ts`
around lines 105 - 119, Add a test case alongside the existing
insufficient-space test for validateSpaceMock resolving with an error response,
and assert that TemporalFileUploader.run rejects with cause BAD_RESPONSE and the
corresponding error message. Also verify repository.watchFile,
repository.stream, uploaderFactory.build, and eventBus.publish are not called.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/fuse-daemon/internal/filesystem/file_test.go`:
- Around line 52-59: Update the OperationRelease handler in TestRelease to
continue validating received.ProcessName as well as received.Path. Capture both
request fields or assert ProcessName directly in the handler, preserving
coverage of the processName property while retaining the existing path
verification.

---

Outside diff comments:
In `@src/infra/drive-server/drive-server.error.test.ts`:
- Around line 4-5: Update mapStatusToErrorCause and its callers so generic
empty-file rejection remains distinct from exceeding the permitted empty-file
quota; introduce or preserve a structured quota-specific cause for the “You can
not have more empty files” response, and ensure file creation and override flows
use it for the correct user-facing error.

---

Nitpick comments:
In
`@src/context/storage/TemporalFiles/application/upload/TemporalFileUploader.test.ts`:
- Around line 105-119: Add a test case alongside the existing insufficient-space
test for validateSpaceMock resolving with an error response, and assert that
TemporalFileUploader.run rejects with cause BAD_RESPONSE and the corresponding
error message. Also verify repository.watchFile, repository.stream,
uploaderFactory.build, and eventBus.publish are not called.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: af0171c6-6931-485b-af46-c016629fd513

📥 Commits

Reviewing files that changed from the base of the PR and between 4904b51 and b00ad9e.

📒 Files selected for processing (5)
  • packages/fuse-daemon/internal/filesystem/file_test.go
  • src/context/storage/TemporalFiles/application/upload/TemporalFileUploader.test.ts
  • src/context/storage/TemporalFiles/application/upload/TemporalFileUploader.ts
  • src/context/virtual-drive/files/application/override/FileOverrider.test.ts
  • src/infra/drive-server/drive-server.error.test.ts

Comment on lines 52 to +59
client.OperationRelease: func(w http.ResponseWriter, r *http.Request) {
var received releaseRequest
body, _ := io.ReadAll(r.Body)
_ = json.Unmarshal(body, &received)
select {
case releasePaths <- received.Path:
default:
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Keep asserting ProcessName

The test now forwards only received.Path, so it no longer verifies the processName field promised by TestRelease and represented by releaseRequest. Capture both fields (or assert ProcessName directly in the handler) so regressions in that request property are still detected.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/fuse-daemon/internal/filesystem/file_test.go` around lines 52 - 59,
Update the OperationRelease handler in TestRelease to continue validating
received.ProcessName as well as received.Path. Capture both request fields or
assert ProcessName directly in the handler, preserving coverage of the
processName property while retaining the existing path verification.

@egalvis27
egalvis27 requested a review from AlexisMora July 30, 2026 15:14
@sonarqubecloud

Copy link
Copy Markdown

@egalvis27
egalvis27 merged commit 031268e into main Jul 30, 2026
12 checks passed
@egalvis27
egalvis27 deleted the fix/upload-empty-files branch July 30, 2026 15:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants