Skip to content

Commit 33d3df2

Browse files
authored
Fix handling of 413 server response when uploading media (#30737)
1 parent 21a86a3 commit 33d3df2

File tree

2 files changed

+39
-5
lines changed

2 files changed

+39
-5
lines changed

src/ContentMessages.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,12 @@ import { blobIsAnimated } from "./utils/Image.ts";
6363
const PHYS_HIDPI = [0x00, 0x00, 0x16, 0x25, 0x00, 0x00, 0x16, 0x25, 0x01];
6464

6565
export class UploadCanceledError extends Error {}
66-
export class UploadFailedError extends Error {}
66+
export class UploadFailedError extends Error {
67+
public constructor(cause: any) {
68+
super();
69+
this.cause = cause;
70+
}
71+
}
6772

6873
interface IMediaConfig {
6974
"m.upload.size"?: number;
@@ -367,7 +372,7 @@ export async function uploadFile(
367372
} catch (e) {
368373
if (abortController.signal.aborted) throw new UploadCanceledError();
369374
console.error("Failed to upload file", e);
370-
throw new UploadFailedError();
375+
throw new UploadFailedError(e);
371376
}
372377
if (abortController.signal.aborted) throw new UploadCanceledError();
373378

@@ -386,7 +391,7 @@ export async function uploadFile(
386391
} catch (e) {
387392
if (abortController.signal.aborted) throw new UploadCanceledError();
388393
console.error("Failed to upload file", e);
389-
throw new UploadFailedError();
394+
throw new UploadFailedError(e);
390395
}
391396
if (abortController.signal.aborted) throw new UploadCanceledError();
392397
// If the attachment isn't encrypted then include the URL directly.
@@ -638,15 +643,18 @@ export default class ContentMessages {
638643
dis.dispatch<UploadFinishedPayload>({ action: Action.UploadFinished, upload });
639644
dis.dispatch({ action: "message_sent" });
640645
} catch (error) {
646+
// Unwrap UploadFailedError to get the underlying error
647+
const unwrappedError = error instanceof UploadFailedError && error.cause ? error.cause : error;
648+
641649
// 413: File was too big or upset the server in some way:
642650
// clear the media size limit so we fetch it again next time we try to upload
643-
if (error instanceof HTTPError && error.httpStatus === 413) {
651+
if (unwrappedError instanceof HTTPError && unwrappedError.httpStatus === 413) {
644652
this.mediaConfig = null;
645653
}
646654

647655
if (!upload.cancelled) {
648656
let desc = _t("upload_failed_generic", { fileName: upload.fileName });
649-
if (error instanceof HTTPError && error.httpStatus === 413) {
657+
if (unwrappedError instanceof HTTPError && unwrappedError.httpStatus === 413) {
650658
desc = _t("upload_failed_size", {
651659
fileName: upload.fileName,
652660
});

test/unit-tests/ContentMessages-test.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { mocked } from "jest-mock";
1010
import {
1111
type ISendEventResponse,
1212
type MatrixClient,
13+
MatrixError,
1314
RelationType,
1415
type UploadResponse,
1516
} from "matrix-js-sdk/src/matrix";
@@ -20,6 +21,9 @@ import ContentMessages, { UploadCanceledError, uploadFile } from "../../src/Cont
2021
import { doMaybeLocalRoomAction } from "../../src/utils/local-room";
2122
import { createTestClient, flushPromises, mkEvent } from "../test-utils";
2223
import { BlurhashEncoder } from "../../src/BlurhashEncoder";
24+
import Modal from "../../src/Modal";
25+
import ErrorDialog from "../../src/components/views/dialogs/ErrorDialog";
26+
import { _t } from "../../src/languageHandler";
2327

2428
jest.mock("matrix-encrypt-attachment", () => ({ encryptAttachment: jest.fn().mockResolvedValue({}) }));
2529

@@ -291,6 +295,28 @@ describe("ContentMessages", () => {
291295
}),
292296
);
293297
});
298+
299+
it("handles 413 error", async () => {
300+
mocked(client.uploadContent).mockRejectedValue(
301+
new MatrixError(
302+
{
303+
errcode: "M_TOO_LARGE",
304+
error: "File size limit exceeded",
305+
},
306+
413,
307+
),
308+
);
309+
const file = new File([], "fileName", { type: "image/jpeg" });
310+
const dialogSpy = jest.spyOn(Modal, "createDialog");
311+
await contentMessages.sendContentToRoom(file, roomId, undefined, client, undefined);
312+
expect(dialogSpy).toHaveBeenCalledWith(
313+
ErrorDialog,
314+
expect.objectContaining({
315+
description: _t("upload_failed_size", { fileName: "fileName" }),
316+
}),
317+
);
318+
dialogSpy.mockRestore();
319+
});
294320
});
295321

296322
describe("getCurrentUploads", () => {

0 commit comments

Comments
 (0)