Repository navigation
Refuse agent uploads over the hosted Clips size limit up front - #7697
Conversation
create-recording-upload advertised 2 GiB, but Clips' free hosted storage caps a clip at maxClipBytes (250 MB by default), enforced only as chunks arrive. The action now passes sizeBytes to create-recording, which refuses a file over the storage's limit with 413 upload_too_large before creating the row, and the tool description states both limits.
|
Here's a visual recap of what changed: Open the full interactive recap |
A failed hosted-allowance release no longer replaces the 413 with a generic error: it is logged and the refusal still reaches the caller. Sizes now round up and limits down, so a file one byte over never reads as fitting, and the tool text states the configured connected-storage limit instead of a fixed 2 GiB.
|
On the three low findings:
|
Releasing a hosted grant after a size refusal could itself fail, and a grant with no recording row is never reclaimed. resolveClipsVideoStorage now takes the file size and refuses a file over the hosted limit before it reserves anything, so there is nothing to release. create-recording's schema caps sizeBytes at MAX_UPLOAD_BYTES for connected storage, and the upload tool points agents at get-video-storage-status for the configured hosted limit.
|
On the two low findings in the second review:
|
create-recording still writes its recovery-policy row before it resolves storage, so "before anything is created" overstated it.
|
On the low finding in the third review: the wording was too broad, and I've narrowed it in 70df857. The tool description now says a larger file is refused "before the recording is created", and |
That action is not in Clips' compact MCP catalog, so an outside agent reading this description cannot call it. The refusal already states the configured limit.
|
Correction to my earlier reply about the hosted limit in the tool text: I've taken out the |
|
Both low notes in the ed6ff85 review are known and left as they are on purpose. The recovery-policy row is explained in my earlier comment and in the PR body under Safety. On the hosted limit's source: no tool in Clips' compact MCP catalog reports the configured |
There was a problem hiding this comment.
Builder reviewed your changes and has a few items to flag 🟡
Review Details
Incremental review of the latest PR #7697 diff: the size check still runs only on the hosted-storage path and before hosted grant reservation; connected-storage uploads bypass it, and the existing byte limit remains consistent with chunk/finalize enforcement. The prior hosted-quota-release concern therefore remains addressed. Both reviewers confirmed one remaining low-severity issue: recovery-policy state is snapshotted before storage resolution can reject an oversized upload, leaving an orphaned app-state row for that recording ID. I found no further actionable issue in the current diff. Risk remains Standard because this changes upload validation and quota behavior.
🧪 Browser testing: Skipped — the PR changes backend storage resolution, action schemas, tests, and agent guidance; no browser UI impact.

Problem
Agents upload local video files to Clips with
create-recording-upload(added in #7571). The tool description and itssizeBytesfield say a file can be up to 2 GiB. That is true only for accounts with their own video storage connected (Builder.io or S3-compatible).Accounts on Clips' free hosted storage (#7624) have a much smaller per-clip limit: 250 MB by default, set by
CLIPS_HOSTED_STORAGE_MAX_CLIP_BYTES. That limit is a storage budget for the free tier, not a technical ceiling, and this PR keeps it as it is. But nothing told the agent about it. A hosted account could start uploading a 600 MB file, have the chunk route accept the first ~250 MB, and then fail partway through with "Recording exceeds the 250 MB size limit". The agent had no way to know beforehand, and the user was left with a failed clip.Approach
State the real limits up front, and refuse a file that can't fit before a hosted grant is reserved or the recording is created. (The small recovery-policy row described under Safety is still written first.)
The agent already sends
sizeByteswhen it asks for an upload. Which limit applies depends on where the clip will be stored, and that decision is made in one place:resolveClipsVideoStorage. It picks the user's own storage when one is connected, and otherwise reserves one of the account's free hosted clips. The size now travels throughcreate-recordingto that resolver. If the clip would go to hosted storage and the file is over the hosted limit, the resolver refuses with a 413 and error codeupload_too_largebefore it reserves the free-clip grant. So a refusal never has to release anything, and can't leave a grant held by no recording. The message gives the caller's actual limit and says that connecting your own storage raises it. For connected storage,create-recording's schema capssizeBytesatMAX_UPLOAD_BYTES, as the upload action's schema already did.The chunk route still enforces the limit on the bytes it actually receives. This PR adds an earlier refusal; it doesn't replace that one.
What changed
create-recording-upload's description and itssizeBytesfield now state both limits. They give the connected-storage limit as configured (MAX_UPLOAD_BYTES, 2 GB unlessCLIPS_MAX_UPLOAD_BYTESchanges it) and the hosted limit as 250 MB by default. A refusal always states the configured limit.resolveClipsVideoStoragetakes an optionalsizeBytesand refuses an oversized hosted file before reserving. It checks only after confirming the account would actually use hosted storage, so accounts with their own storage, or with hosted storage turned off, are unaffected.detailscarrysizeBytes,maxUploadBytesandstorageMode.create-recordingpassessizeBytesthrough, and its schema caps it atMAX_UPLOAD_BYTES.formatUploadBytesinshared/upload-limits.tsrounds a file's size up and a limit down, so a file one byte over the limit reads "250.1 MB … up to 250 MB", never as fitting.fixedentry in Clips' changelog.Browser uploads are unchanged. The record page already checks hosted limits on the client, and it doesn't send
sizeBytes.Safety and operations
sizeBytesis optional on bothcreate-recordingand the resolver, so existing callers (recorders, desktop, extension, mobile, Loom import, chunk reset) behave as before.create-recordingwrites before it resolves storage. Nothing deletes these rows for any recording today, and moving the write after storage resolution would let a failed write strand a hosted reservation. So this PR leaves the order alone.Verification
video-storage.hosted.test.ts): on hosted storage, a file one byte over 250 MB is refused with 413upload_too_large, the limit in the message and details, and no grant reserved. A file of exactly 250 MB reserves and resolves as hosted. A 300 MB file resolves to the user's own storage when one is connected. With the check removed, the refusal test fails.create-recordingtest: the size is handed to the resolver, and when the resolver refuses, nothing is inserted and no upload state is written.create-recording-upload.test.tsasserts that the size is passed through.pnpm typecheckfor Clips passes with no errors.guard:i18n-catalogs. It couldn't import three templates outside Clips (Assets, Content and Slides), because this worktree only had Clips' dependencies installed, and it reported nothing in Clips. CI runs it on a full install.guard:i18n-changed-copypasses.Review focus
resolveClipsVideoStoragethe right place? It is the only place that knows both the storage mode and whether a hosted grant is about to be reserved.