diff --git a/src/storage/utils/object.js b/src/storage/utils/object.js index 18ca3e36..fc5367da 100644 --- a/src/storage/utils/object.js +++ b/src/storage/utils/object.js @@ -35,5 +35,5 @@ export async function notifyCollab(api, url, env) { // method: 'POST', headers, }); - resp.body.cancel(); + resp.body?.cancel(); } diff --git a/test/storage/utils/object.test.js b/test/storage/utils/object.test.js index 4606f34f..3ce432ff 100644 --- a/test/storage/utils/object.test.js +++ b/test/storage/utils/object.test.js @@ -46,6 +46,22 @@ describe('Storage Object Utils tests', () => { assert.strictEqual(called.length, 0, 'should not have invalidated anything'); }); + it('does not throw when collab response has null body', async () => { + // Regression test: notifyCollab calls resp.body.cancel() unconditionally. When the collab + // service returns a response with no body (null), this throws TypeError: Cannot read + // properties of null (reading 'cancel'). In copyFile this fires in a finally block, + // which cancels the return value and causes the move to fail with partial_failure 500 — + // leaving the file duplicated at both source and destination. + const env = { + dacollab: { + fetch: async () => ({ body: null }), + }, + }; + await assert.doesNotReject( + notifyCollab('syncadmin', 'https://admin.da.live/source/a/b/c.html', env), + ); + }); + it('Should invalidate (with shared secret', async () => { const called = []; const env = {