Commit 5ecc178
committed
fix(copilot): clamp the legacy int4 size when materializing a chat upload
`materialize_file(operation: 'save')` wrote the HEADed object size straight into
`workspace_files.size`, which is still `integer NOT NULL`. Since the `size_bytes`
widening (0289), a mothership chat attachment may be up to MAX_WORKSPACE_FILE_SIZE
(5 GiB): `upload-session/service.ts` gives `mothership_attachment` that ceiling, and
`finalizers.ts` already dual-writes the row as `size = 2147483647,
size_bytes = <exact>`. Saving such an upload then re-read the true size from
`headObject` and issued `SET size = 3221225472` against int4. Postgres raises 22003;
the retry filter matches only 23505, so it rethrows, the transaction rolls back and
the tool returns `success: false` with no way for the user to complete the save. No
corruption — int4 overflow errors, it never truncates — but the file can never be
saved.
Every other `workspace_files` size writer already pairs
`toLegacyWorkspaceFileSize(bytes)` with `sizeBytes: bytes` (metadata.ts x4,
workspace-file-manager.ts:243/1706, finalizers.ts:367). This call site was simply
missed when the widening landed; the fix converges it with the other six rather than
inventing a third shape. Storage accounting keeps using the exact `verifiedSize`, so
quota and usage are unaffected.
The size source itself also had to widen. `head?.size ?? row.size` fell back to the
clamped int4 column, and since this change now writes `sizeBytes` too, that fallback
would overwrite an exact `size_bytes` with the clamp — the object is gone, so nothing
could recover it, and the row would look internally consistent afterwards. The
fallback is live whenever `hasCloudStorage()` is false, since the early return at the
HEAD miss is cloud-only. Reading `row.sizeBytes ?? row.size` is the same coalescing
shape the readers already use (workspace-file-manager.ts:227, finalizers.ts:399,
metadata.ts:46), and the row comes from a full `select()` so the column is present.
The clamp is derived once next to `verifiedSize` rather than inline in the update
because the value is loop-invariant.
Two sibling writers were examined and deliberately left alone. `copy-files.ts` reads
`task.size` out of the int4 column itself, so it is arithmetically incapable of
overflow, and its missing `sizeBytes` is unreachable behind the 100 MB fork download
cap. `workspace-file-manager.ts:963` takes a caller-supplied size, but its insert
branch needs an orphaned storage object with no `workspace_files` row, and converting
loose external input from a DB error into a JS throw deserves its own review rather
than a release patch; it is the next instance of this bug and should be filed as a
follow-up.
Both new tests were proven red against the unfixed code: the clamp test fails with
"expected 3221225472 to be 2147483647", the fallback test with
"expected undefined to be 3221225472".1 parent b5d9e93 commit 5ecc178
2 files changed
Lines changed: 64 additions & 2 deletions
Lines changed: 55 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
155 | 155 | | |
156 | 156 | | |
157 | 157 | | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
158 | 161 | | |
159 | 162 | | |
160 | 163 | | |
| |||
339 | 342 | | |
340 | 343 | | |
341 | 344 | | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
342 | 397 | | |
343 | 398 | | |
344 | 399 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
36 | 36 | | |
37 | 37 | | |
38 | 38 | | |
| 39 | + | |
39 | 40 | | |
40 | 41 | | |
41 | 42 | | |
| |||
109 | 110 | | |
110 | 111 | | |
111 | 112 | | |
112 | | - | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
113 | 119 | | |
114 | 120 | | |
115 | 121 | | |
| |||
145 | 151 | | |
146 | 152 | | |
147 | 153 | | |
148 | | - | |
| 154 | + | |
| 155 | + | |
149 | 156 | | |
150 | 157 | | |
151 | 158 | | |
| |||
0 commit comments