fix(api): add retry for document blob head checks

Implement a retry mechanism for document blob HEAD requests to handle
S3 read-after-write propagation delays. This prevents 409 errors
when the client calls the endpoint immediately after performing a
direct upload to S3 via a presigned URL.
This commit is contained in:
Richard R 2026-02-16 15:51:01 -07:00
parent 596c04d4d3
commit 9931c7d0d8

View file

@ -84,19 +84,39 @@ export async function POST(req: NextRequest) {
for (const doc of documentsData) { for (const doc of documentsData) {
let headSize = doc.size; let headSize = doc.size;
try {
const head = await headDocumentBlob(doc.id, testNamespace); // Retry HEAD check to handle S3 read-after-write propagation delays.
if (head.contentLength > 0) headSize = head.contentLength; // The client uploads bytes directly to S3 via presigned URL, then
} catch (error) { // immediately calls this endpoint. On serverless platforms the HEAD
if (isMissingBlobError(error)) { // request may reach S3 before the PUT is visible.
return NextResponse.json( const HEAD_RETRIES = 3;
{ const HEAD_RETRY_DELAY_MS = 500;
error: `Blob missing for document ${doc.id}. Upload bytes first using /api/documents/blob/upload/presign.`, let headError: unknown = null;
}, for (let attempt = 0; attempt < HEAD_RETRIES; attempt++) {
{ status: 409 }, headError = null;
); try {
const head = await headDocumentBlob(doc.id, testNamespace);
if (head.contentLength > 0) headSize = head.contentLength;
break;
} catch (error) {
if (isMissingBlobError(error)) {
headError = error;
if (attempt < HEAD_RETRIES - 1) {
await new Promise((r) => setTimeout(r, HEAD_RETRY_DELAY_MS));
continue;
}
} else {
throw error;
}
} }
throw error; }
if (headError && isMissingBlobError(headError)) {
return NextResponse.json(
{
error: `Blob missing for document ${doc.id}. Upload bytes first using /api/documents/blob/upload/presign.`,
},
{ status: 409 },
);
} }
await db await db