From 041d2191da4410a08e83dcfd5861f5f8ceebff67 Mon Sep 17 00:00:00 2001 From: digua Date: Thu, 16 Jul 2026 19:46:41 +0800 Subject: [PATCH] fix(http): preserve batch delete client errors Let status-coded 4xx errors reach the shared Fastify handler so missing sessions remain distinguishable from transaction failures. Add regression coverage for the missing-session response. --- .../http-routes/src/routes/shared-routes.test.ts | 16 ++++++++++++++++ packages/http-routes/src/routes/web/members.ts | 12 +++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) 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 + ) +}