Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/improve-asset-upload-retry-log.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"wrangler": patch
---

Improve the log message shown when an asset upload attempt fails and is retried

The retry message now reports which attempt is being made (e.g. `Asset upload failed. Retrying... 1 of 5 attempts.`), making it easier to gauge how close Wrangler is to exhausting its retry budget. The raw error object is no longer appended to this user-facing message; it is instead logged at debug level (visible via `WRANGLER_LOG=debug`).
71 changes: 71 additions & 0 deletions packages/wrangler/src/__tests__/deploy/assets.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1326,5 +1326,76 @@ describe("deploy", () => {
`[Error: An unexpected response has been received from the Cloudflare API for assets upload. Please try again.]`
);
});

it("should retry asset uploads on failure and log a retry message including the attempt count", async ({
expect,
}) => {
vi.stubEnv("WRANGLER_LOG", "debug");

const assets = [{ filePath: "file-1.txt", content: "Content of file-1" }];
writeAssets(assets);
writeWranglerConfig({
assets: { directory: "assets" },
});

const mockBuckets = [["0de3dd5df907418e9730fd2bd747bd5e"]];
await mockAUSRequest([], mockBuckets, "<<aus-token>>");

// Fail the first upload attempt, succeed on the second.
const uploadAttempts: Request[] = [];
msw.use(
http.post(
"*/accounts/some-account-id/workers/assets/upload",
async ({ request }) => {
uploadAttempts.push(request);
if (uploadAttempts.length === 1) {
return HttpResponse.json(
{
success: false,
errors: [{ code: 1, message: "upload-boom-from-test" }],
messages: [],
result: null,
},
{ status: 500 }
);
}
return HttpResponse.json(
{
success: true,
errors: [],
messages: [],
result: { jwt: "<<aus-completion-token>>" },
},
{ status: 201 }
);
}
)
);

mockSubDomainRequest();
mockUploadWorkerRequest({
expectedAssets: {
jwt: "<<aus-completion-token>>",
config: {},
},
expectedType: "none",
});

await runWrangler("deploy");

// The upload endpoint was hit twice: once failing, once succeeding.
expect(uploadAttempts.length).toBe(2);

// The new info message includes the attempt count.
expect(std.info).toContain(
"Asset upload failed. Retrying... 1 of 5 attempts."
);

// The error details no longer leak into the user-facing info log.
expect(std.info).not.toContain("upload-boom-from-test");

// The error is now logged at debug level instead.
expect(std.debug).toContain("upload-boom-from-test");
});
});
});
7 changes: 6 additions & 1 deletion packages/wrangler/src/assets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,12 @@ export const syncAssets = async (
return res;
} catch (e) {
if (attempts < MAX_UPLOAD_ATTEMPTS) {
logger.info(chalk.dim(`Asset upload failed. Retrying...\n`, e));
logger.info(
chalk.dim(
`Asset upload failed. Retrying... ${attempts + 1} of ${MAX_UPLOAD_ATTEMPTS} attempts.\n`
)
);
logger.debug(e);
// Exponential backoff, 1 second first time, then 2 second, then 4 second etc.
await new Promise((resolvePromise) =>
setTimeout(resolvePromise, Math.pow(2, attempts) * 1000)
Expand Down
Loading