Problem
The generated create/update handlers stream the uploaded file to the blob store during multipart parsing (autumn-cli/src/generate/scaffold.rs ~2089, field.save_to_blob_store(...)) — i.e. before changeset validation (~2355 create / ~2445 update) and before the DB write. So every early return leaves the just-saved blob orphaned in the store:
- 422 invalid changeset,
- unique-violation 422 (~2322 create / ~2382 update),
- generic DB
Err (~2329 create / ~2389 update).
The blob is leaked on ordinary invalid submissions, not just on rare error paths.
Available primitives
BlobStore::delete (autumn/src/storage/mod.rs:279) is a no-op if the key is absent, and is available on the store the handler already holds.
Blob.key is public.
Framework context (informs the fix)
The direct-upload path deliberately tolerates orphans: it relies on bind-step promotion and delegates cleanup to a periodic sweep / lifecycle policy (autumn/src/storage/direct_upload.rs:16-32). So the fix here should be a deliberate, consistent design choice rather than an ad-hoc patch, choosing between:
- Eager best-effort delete on all early-return paths — careful to distinguish a newly-saved blob from a preserved existing blob on update (update's
.or(current.image) must not delete the preserved one), or
- A documented sweep matching the direct-upload path's model.
Scope
Cover all early returns (422 validation, unique-violation 422, generic DB error) on both create and update — not just the 422 path.
Found during review of #1867 (Part of #1236). Sibling to the CSRF follow-up #1866.
Problem
The generated create/update handlers stream the uploaded file to the blob store during multipart parsing (
autumn-cli/src/generate/scaffold.rs~2089,field.save_to_blob_store(...)) — i.e. before changeset validation (~2355 create / ~2445 update) and before the DB write. So every early return leaves the just-saved blob orphaned in the store:Err(~2329 create / ~2389 update).The blob is leaked on ordinary invalid submissions, not just on rare error paths.
Available primitives
BlobStore::delete(autumn/src/storage/mod.rs:279) is a no-op if the key is absent, and is available on the store the handler already holds.Blob.keyis public.Framework context (informs the fix)
The direct-upload path deliberately tolerates orphans: it relies on bind-step promotion and delegates cleanup to a periodic sweep / lifecycle policy (
autumn/src/storage/direct_upload.rs:16-32). So the fix here should be a deliberate, consistent design choice rather than an ad-hoc patch, choosing between:.or(current.image)must not delete the preserved one), orScope
Cover all early returns (422 validation, unique-violation 422, generic DB error) on both create and update — not just the 422 path.
Found during review of #1867 (Part of #1236). Sibling to the CSRF follow-up #1866.