Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
bbad4a4
fix: aggregate error messages when both multi-part upload and abort o…
thiyaguk09 Oct 8, 2026
5d90320
fix: improve error message handling in multipart upload failure path …
thiyaguk09 Oct 8, 2026
f693764
Merge branch 'main' into fix/9193-transfer-manager-swallowed-error
thiyaguk09 Oct 9, 2026
88a5bed
Merge branch 'main' into fix/9193-transfer-manager-swallowed-error
thiyaguk09 Oct 12, 2026
b24bcff
Merge branch 'main' into fix/9193-transfer-manager-swallowed-error
thiyaguk09 Oct 13, 2026
4b0926f
Merge branch 'main' into fix/9193-transfer-manager-swallowed-error
thiyaguk09 Oct 14, 2026
17f24ce
Merge branch 'main' into fix/9193-transfer-manager-swallowed-error
thiyaguk09 Oct 15, 2026
ea2e2d6
Merge branch 'main' into fix/9193-transfer-manager-swallowed-error
thiyaguk09 Oct 19, 2026
3e40d78
Merge branch 'main' into fix/9193-transfer-manager-swallowed-error
thiyaguk09 Oct 20, 2026
fa26d86
fix: preserve upload state and error handling logic in multipart uplo…
thiyaguk09 Oct 20, 2026
d0d7dc9
Merge branch 'main' into fix/9193-transfer-manager-swallowed-error
thiyaguk09 Oct 22, 2026
2264954
Merge branch 'main' into fix/9193-transfer-manager-swallowed-error
thiyaguk09 Oct 23, 2026
655867e
Merge branch 'main' into fix/9193-transfer-manager-swallowed-error
thiyaguk09 Oct 23, 2026
148b306
Merge branch 'main' into fix/9193-transfer-manager-swallowed-error
thiyaguk09 Oct 26, 2026
86d30bb
Merge branch 'main' into fix/9193-transfer-manager-swallowed-error
thiyaguk09 Oct 30, 2026
46d50a1
Merge branch 'main' into fix/9193-transfer-manager-swallowed-error
thiyaguk09 Oct 30, 2026
db3e432
Merge branch 'main' into fix/9193-transfer-manager-swallowed-error
thiyaguk09 Nov 2, 2026
50a5a0d
Merge branch 'main' into fix/9193-transfer-manager-swallowed-error
thiyaguk09 Nov 2, 2026
261f453
refactor: add trailing commas in transfer manager tests
thiyaguk09 Nov 2, 2026
5a51dd4
Merge branch 'main' into fix/9193-transfer-manager-swallowed-error
thiyaguk09 Nov 2, 2026
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
13 changes: 5 additions & 8 deletions handwritten/storage/src/transfer-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -964,23 +964,20 @@ export class TransferManager {
await Promise.all(promises);
return await mpuHelper.completeUpload();
} catch (e) {
let uploadMsg = (e as Error)?.message ?? String(e);
if (
(options.autoAbortFailure === undefined || options.autoAbortFailure) &&
mpuHelper.uploadId
) {
try {
await mpuHelper.abortUpload();
return;
Comment thread
thiyaguk09 marked this conversation as resolved.
Resolved
} catch (e) {
throw new MultiPartUploadError(
(e as Error).message,
mpuHelper.uploadId!,
mpuHelper.partsMap!,
);
} catch (abortErr) {
const abortMsg = (abortErr as Error)?.message ?? String(abortErr);
uploadMsg += `\n${abortMsg}`;
}
}
throw new MultiPartUploadError(
(e as Error).message,
uploadMsg,
mpuHelper.uploadId!,
mpuHelper.partsMap!,
);
Expand Down
87 changes: 81 additions & 6 deletions handwritten/storage/test/transfer-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -846,13 +846,14 @@ describe('Transfer Manager', () => {
);
});

it('should call abortUpload when a failure occurs after an uploadID is established', async () => {
it('should call abortUpload and reject with the original error when a failure occurs after an uploadID is established', async () => {
const fakeId = '123';
const fakePartsMap = new Map<number, string>([[1, 'etag-1']]);
const expectedErr = new MultiPartUploadError(
'Hello World',
'',
new Map<number, string>(),
fakeId,
fakePartsMap,
);
const fakeId = '123';

mockGeneratorFunction = (bucket, fileName, uploadId, partsMap) => {
fakeHelper = sandbox.createStubInstance(FakeXMLHelper);
Expand All @@ -861,7 +862,8 @@ describe('Transfer Manager', () => {
fakeHelper.initiateUpload.resolves();
fakeHelper.uploadPart.callsFake(() => {
fakeHelper.uploadId = fakeId;
return Promise.reject(expectedErr);
fakeHelper.partsMap = fakePartsMap;
return Promise.reject(new Error(expectedErr.message));
});
fakeHelper.completeUpload.resolves();
fakeHelper.abortUpload.callsFake(() => {
Expand All @@ -871,9 +873,82 @@ describe('Transfer Manager', () => {
return fakeHelper;
};

assert.doesNotThrow(() =>
await assert.rejects(
Comment thread
thiyaguk09 marked this conversation as resolved.
Resolved
transferManager.uploadFileInChunks(filePath, {}, mockGeneratorFunction),
expectedErr,
);
assert.strictEqual(fakeHelper.abortUpload.calledOnce, true);
});

it('should not call abortUpload when autoAbortFailure is false and a failure occurs after an uploadID is established', async () => {
const fakeId = '123';
const fakePartsMap = new Map<number, string>([[1, 'etag-1']]);
const expectedErr = new MultiPartUploadError(
'Hello World',
fakeId,
fakePartsMap,
);

mockGeneratorFunction = (bucket, fileName, uploadId, partsMap) => {
fakeHelper = sandbox.createStubInstance(FakeXMLHelper);
fakeHelper.uploadId = uploadId || '';
fakeHelper.partsMap = partsMap || new Map<number, string>();
fakeHelper.initiateUpload.resolves();
fakeHelper.uploadPart.callsFake(() => {
fakeHelper.uploadId = fakeId;
fakeHelper.partsMap = fakePartsMap;
return Promise.reject(new Error(expectedErr.message));
});
fakeHelper.completeUpload.resolves();
fakeHelper.abortUpload.resolves();
return fakeHelper;
};

await assert.rejects(
transferManager.uploadFileInChunks(
filePath,
{autoAbortFailure: false},
mockGeneratorFunction,
),
expectedErr,
);
assert.strictEqual(fakeHelper.abortUpload.notCalled, true);
});

it('should reject with an error containing both messages when both uploadPart and abortUpload fail', async () => {
const fakeId = '123';
const fakePartsMap = new Map<number, string>([[1, 'etag-1']]);
const uploadErr = new Error('Chunk upload failed');
const abortErr = new Error('Abort upload failed');
const expectedErr = new MultiPartUploadError(
`${uploadErr.message}\n${abortErr.message}`,
fakeId,
fakePartsMap,
);

mockGeneratorFunction = (bucket, fileName, uploadId, partsMap) => {
fakeHelper = sandbox.createStubInstance(FakeXMLHelper);
fakeHelper.uploadId = uploadId || '';
fakeHelper.partsMap = partsMap || new Map<number, string>();
fakeHelper.initiateUpload.resolves();
fakeHelper.uploadPart.callsFake(() => {
fakeHelper.uploadId = fakeId;
fakeHelper.partsMap = fakePartsMap;
return Promise.reject(uploadErr);
});
fakeHelper.completeUpload.resolves();
fakeHelper.abortUpload.callsFake(() => {
assert.strictEqual(fakeHelper.uploadId, fakeId);
return Promise.reject(abortErr);
});
return fakeHelper;
};

await assert.rejects(
transferManager.uploadFileInChunks(filePath, {}, mockGeneratorFunction),
expectedErr,
);
assert.strictEqual(fakeHelper.abortUpload.calledOnce, true);
});

it('should set the appropriate `GCCL_GCS_CMD_KEY`', async () => {
Expand Down
Loading