diff --git a/packages/http-routes/src/routes/shared-routes.test.ts b/packages/http-routes/src/routes/shared-routes.test.ts index 65abdb82..668c7e2c 100644 --- a/packages/http-routes/src/routes/shared-routes.test.ts +++ b/packages/http-routes/src/routes/shared-routes.test.ts @@ -388,6 +388,22 @@ describe('registerSharedRoutes smoke tests', () => { assert.equal(memberCount.count, 2) }) + it('POST /_web/sessions/:id/members/batch-delete preserves missing-session errors', async () => { + const routeApp = Fastify() + registerSharedRoutes(routeApp, createTestContext()) + await routeApp.ready() + + const resp = await routeApp.inject({ + method: 'POST', + url: '/_web/sessions/missing/members/batch-delete', + payload: { memberIds: [1] }, + }) + + await routeApp.close() + + assert.equal(resp.statusCode, 404) + }) + it('POST /_web/sessions/:id/members/batch-delete rolls back every deletion when one member fails', async () => { const db = createSessionDb() db.exec(` diff --git a/packages/http-routes/src/routes/web/members.ts b/packages/http-routes/src/routes/web/members.ts index 9c1ba8cf..95ace206 100644 --- a/packages/http-routes/src/routes/web/members.ts +++ b/packages/http-routes/src/routes/web/members.ts @@ -64,7 +64,7 @@ export function registerMemberRoutes(server: FastifyInstance, ctx: MemberRouteCo throw new Error('Member deletion transaction returned no result') } } catch (error) { - if (apiErrorFromUnknown(error)) throw error + if (apiErrorFromUnknown(error) || isHttpClientError(error)) throw error appLogger.error('members', 'Batch member deletion failed', error) return reply.code(500).send({ success: false, error: 'Failed to delete selected members' }) } @@ -94,3 +94,13 @@ export function registerMemberRoutes(server: FastifyInstance, ctx: MemberRouteCo } ) } + +function isHttpClientError(error: unknown): error is Error & { statusCode: number } { + return ( + error instanceof Error && + 'statusCode' in error && + typeof error.statusCode === 'number' && + error.statusCode >= 400 && + error.statusCode < 500 + ) +}