Skip to content

Harden workspace upload failure handling#669

Merged
urjitc merged 3 commits into
mainfrom
agent/codemode-runtime-contracts
Jul 21, 2026
Merged

Harden workspace upload failure handling#669
urjitc merged 3 commits into
mainfrom
agent/codemode-runtime-contracts

Conversation

@urjitc

@urjitc urjitc commented Jul 21, 2026

Copy link
Copy Markdown
Member

What changed

  • remove the duplicate global PostHog browser error listeners while retaining automatic and boundary capture
  • replace the upload AsyncQueuer callback bridge with a bounded worker pool
  • preserve original per-file upload failures and emit one sanitized batch exception
  • cap workspace uploads, selections, and converter intake at 100 MB
  • update upload documentation and focused boundary tests

Why

Production telemetry showed direct browser-to-R2 upload failures being duplicated and then flattened into a generic error. The prior queue callbacks could also misclassify cache-update failures as upload failures. Cloudflare recommends single PUT for files around this size range; the 100 MB guardrail avoids the previous 200 MB reliability and memory mismatch.

Impact

Users receive the same batch toast behavior, upload errors retain useful sanitized context, cancellations stay out of error tracking, and files above 100 MB are rejected with a 413 before processing.

The separate staging R2 CORS drift was repaired directly in Cloudflare using the checked-in config/r2/cors.staging.json policy and verified through Wrangler and the Cloudflare API.

Validation

  • pnpm check
  • 12 focused Vitest tests
  • git diff --check
  • React Doctor changed-scope scan: no issues

Review in cubic


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.

Summary by CodeRabbit

  • Changes

    • Reduced the maximum workspace upload size from 200 MB to 100 MB per file and selection.
    • Reduced file import upload concurrency from 5 to 3.
    • Upload validation now clearly reports when the 100 MB limit is exceeded.
    • Improved batch upload handling with per-file success and failure feedback, cancellation support, and clearer error notifications.
    • Upload failures are reported more consistently, while cancellations avoid unnecessary error reporting.
  • Documentation

    • Updated workspace and file import limits to reflect the new 100 MB maximum and concurrency limit.

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Upload limits are reduced from 200 MB to 100 MB across workspace validation, container services, and documentation. Workspace batch uploads now return per-file outcomes, report failures explicitly, support cancellation, and preserve callback errors. PostHog global browser error capture is removed.

Changes

Workspace upload behavior

Layer / File(s) Summary
Upload limits and validation
src/features/workspaces/model/workspace-file/limits.ts, src/features/workspaces/upload/*, containers/*, docs/...
Workspace and service upload thresholds are reduced to 100 MB, validation messages and boundary tests are updated, and import concurrency documentation changes from 5 to 3.
Batch upload outcomes and errors
src/features/workspaces/files/workspace-file-upload.ts, src/features/workspaces/components/WorkspaceFileUploadProvider.tsx, src/features/workspaces/use-workspace-client-mutation-echo.ts
Batch uploads use worker-pool concurrency, per-file outcomes, cancellation detection, explicit error reporting, and a void return contract.
Batch failure validation
src/features/workspaces/files/workspace-file-upload.test.ts
Tests cover upload failures, abort cancellation, exception metadata, and callback errors that propagate without being classified as upload failures.

PostHog provider composition

Layer / File(s) Summary
Provider error capture removal
src/integrations/posthog/provider.tsx
Global browser error listeners and error normalization are removed while authentication synchronization remains mounted.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant WorkspaceFileUploadProvider
  participant runWorkspaceFileUploadBatch
  participant uploadAcceptedFiles
  participant uploadFileDirectlyToR2
  participant PostHog
  WorkspaceFileUploadProvider->>runWorkspaceFileUploadBatch: start accepted-file batch
  runWorkspaceFileUploadBatch->>uploadAcceptedFiles: execute concurrent uploads
  uploadAcceptedFiles->>uploadFileDirectlyToR2: upload file
  uploadFileDirectlyToR2-->>uploadAcceptedFiles: success or error outcome
  uploadAcceptedFiles-->>runWorkspaceFileUploadBatch: return per-file outcomes
  runWorkspaceFileUploadBatch->>PostHog: capture first non-abort failure
  runWorkspaceFileUploadBatch-->>WorkspaceFileUploadProvider: show success or upload error
Loading

Possibly related PRs

  • ThinkEx-OSS/thinkex#643: Updates the workspace upload limit model and related enforcement in the same upload pipeline.
🚥 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 clearly matches the main change: improving workspace upload failure handling.
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
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch agent/codemode-runtime-contracts

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.

@github-actions

Copy link
Copy Markdown

React Doctor found no new issues. 🎉

Reviewed by React Doctor for commit 133d7ee.

@urjitc
urjitc marked this pull request as ready for review July 21, 2026 16:36
@cursor

cursor Bot commented Jul 21, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@urjitc
urjitc merged commit 7f79c45 into main Jul 21, 2026
11 of 12 checks passed
@github-project-automation github-project-automation Bot moved this from Backlog to Done in Dev Board Jul 21, 2026

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 133d7eef96

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

const execFileAsync = promisify(execFile);
const port = 8080;
const maxInputBytes = 200 * 1024 * 1024;
const maxInputBytes = 100 * 1024 * 1024;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Leave room for multipart overhead in image uploads

When a HEIC/HEIF workspace upload is near the advertised 100 MiB limit, intake accepts it because it compares File.size to workspaceFileUploadLimits.maxFileBytes, but this container compares the HTTP content-length against the same 100 MiB. The conversion client sends the file as multipart/form-data, so the boundary/header bytes make an otherwise valid 100 MiB file exceed this limit and fail during conversion instead of uploading successfully; either allow overhead here or validate the parsed file's size.

Useful? React with 👍 / 👎.

@greptile-apps

greptile-apps Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR hardens workspace upload failure handling and lowers upload limits. The main changes are:

  • Workspace upload, selection, and converter limits now use a 100 MB cap.
  • Batch uploads now use a bounded worker pool with consolidated failure reporting.
  • Upload cancellation is excluded from client exception capture.
  • PostHog duplicate global browser error listeners were removed.
  • Upload documentation and focused Vitest coverage were updated.

Confidence Score: 5/5

This PR is safe to merge with low risk.

Upload limit enforcement is consistent across client validation, server-side intake, converter services, and documentation. The upload worker rewrite preserves bounded concurrency and the reviewed tests cover failure classification and cancellation behavior. No review comments were identified.

No files require special attention.

T-Rex T-Rex Logs

What T-Rex did

  • Ran the Vitest test suite for the workspace-upload-focused flow and confirmed the run completed with all tests passing.
  • Checked the git diff --check output and confirmed there were no issues detected.
  • Ran pnpm check and found formatting issues in specific greptile-related files, causing the check to exit with a non-zero status.
  • Ran pnpm doctor and observed no feature branch or uncommitted changes, plus a full scan with unrelated/generated-file diagnostics, but the command exited with a non-zero status.

View all artifacts

T-Rex Ran code and verified through T-Rex

Important Files Changed

Filename Overview
src/features/workspaces/files/workspace-file-upload.ts Replaced AsyncQueuer with bounded workers and consolidated upload failure reporting; no issues found.
src/features/workspaces/upload/workspace-upload-intake.ts Updated validation messages and enforcement to the new 100 MB workspace upload cap; no issues found.
src/features/workspaces/model/workspace-file/limits.ts Lowered per-file and per-selection upload limits to 100 MB and kept concurrency at 3; no issues found.
src/integrations/posthog/provider.tsx Removed duplicate global browser error listeners while retaining PostHog provider integration; no issues found.
src/features/workspaces/components/WorkspaceFileUploadProvider.tsx Removed swallowing of batch upload promise rejections so non-upload callback failures can surface; no issues found.
containers/liteparse/server.mjs Reduced LiteParse request byte limit from 200 MB to 100 MB; no issues found.
containers/image-converter/server.mjs Reduced image converter maximum input size from 200 MB to 100 MB; no issues found.

Reviews (1): Last reviewed commit: "fix(uploads): cap workspace uploads at 1..." | Re-trigger Greptile

@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

🧹 Nitpick comments (1)
src/features/workspaces/files/workspace-file-upload.test.ts (1)

34-50: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Clean up stubbed globals.

While Vitest isolates test environments per file by default, it is a best practice to clean up any globally stubbed objects (like fetch in this case) after each test. This prevents potential state leakage if isolation settings change or if tests that require the original global state are added to this suite later.

♻️ Proposed refactor to add `afterEach` cleanup

Update your import to include afterEach:

-import { beforeEach, describe, expect, it, vi } from "vitest";
+import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";

And add the hook after beforeEach:

 		),
 	);
 });
+
+afterEach(() => {
+	vi.unstubAllGlobals();
+});
 
 describe("workspace file upload batch failures", () => {
🤖 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/features/workspaces/files/workspace-file-upload.test.ts` around lines 34
- 50, Add afterEach cleanup for the fetch stub created in beforeEach, using
Vitest’s global-restoration mechanism so the original fetch is restored after
every test. Update the Vitest import to include afterEach and keep the existing
mocked response setup unchanged.
🤖 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/features/workspaces/files/workspace-file-upload.ts`:
- Around line 181-192: Update settleWorkspaceFileUpload to preserve DOMException
values, especially fetch abort errors, when constructing the failed
WorkspaceFileUploadOutcome. Extend the existing error selection so DOMException
instances are returned unchanged, while retaining the current Error handling and
generic fallback for other values.

---

Nitpick comments:
In `@src/features/workspaces/files/workspace-file-upload.test.ts`:
- Around line 34-50: Add afterEach cleanup for the fetch stub created in
beforeEach, using Vitest’s global-restoration mechanism so the original fetch is
restored after every test. Update the Vitest import to include afterEach and
keep the existing mocked response setup unchanged.
🪄 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

Run ID: a36b3afd-ae5e-4694-9fd8-d7abcc69f6b5

📥 Commits

Reviewing files that changed from the base of the PR and between f755480 and 133d7ee.

📒 Files selected for processing (12)
  • containers/image-converter/server.mjs
  • containers/liteparse/server.mjs
  • docs/concepts/items.mdx
  • docs/guides/import-files.mdx
  • src/features/workspaces/components/WorkspaceFileUploadProvider.tsx
  • src/features/workspaces/files/workspace-file-upload.test.ts
  • src/features/workspaces/files/workspace-file-upload.ts
  • src/features/workspaces/model/workspace-file/limits.ts
  • src/features/workspaces/upload/workspace-upload-intake.test.ts
  • src/features/workspaces/upload/workspace-upload-intake.ts
  • src/features/workspaces/use-workspace-client-mutation-echo.ts
  • src/integrations/posthog/provider.tsx
💤 Files with no reviewable changes (1)
  • src/integrations/posthog/provider.tsx

Comment on lines +181 to 192
async function settleWorkspaceFileUpload(
job: WorkspaceFileUploadJob,
): Promise<WorkspaceFileUploadOutcome> {
try {
return { command: await uploadWorkspaceFile(job), ok: true };
} catch (error) {
return {
error: error instanceof Error ? error : new Error("Unable to upload file."),
ok: false,
};
}
}

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 | 🟠 Major | ⚡ Quick win

Preserve DOMException here. Wrapping anything that isn’t instanceof Error converts fetch aborts into a generic error, so isWorkspaceUploadAbortError no longer recognizes user cancellations and they get reported as failures.

🤖 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/features/workspaces/files/workspace-file-upload.ts` around lines 181 -
192, Update settleWorkspaceFileUpload to preserve DOMException values,
especially fetch abort errors, when constructing the failed
WorkspaceFileUploadOutcome. Extend the existing error selection so DOMException
instances are returned unchanged, while retaining the current Error handling and
generic fallback for other values.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant