diff --git a/apps/sim/app/api/files/serve/[...path]/route.test.ts b/apps/sim/app/api/files/serve/[...path]/route.test.ts index 8b4f9f6a573..7cfca662081 100644 --- a/apps/sim/app/api/files/serve/[...path]/route.test.ts +++ b/apps/sim/app/api/files/serve/[...path]/route.test.ts @@ -34,6 +34,7 @@ import { workspaceFileManagerMock, workspaceFileManagerMockFns, } from '@sim/testing/mocks/workspace-file-manager.mock' +import { NextRequest } from 'next/server' import { beforeEach, describe, expect, it, vi } from 'vitest' import { MAX_BUFFERED_TRANSFER_BYTES } from '@/lib/uploads/shared/types' @@ -325,6 +326,24 @@ describe('File Serve API Route', () => { expect(serveLogger.error).not.toHaveBeenCalled() }) + it('answers 499 without an error log when the client disconnects mid-read', async () => { + const controller = new AbortController() + mockReadLocalFileWithinLimit.mockImplementation(async () => { + controller.abort() + throw new Error('Request aborted') + }) + + const response = await GET( + new NextRequest('http://localhost:3000/api/files/serve/workspace/ws/report.md', { + signal: controller.signal, + }), + createRouteContext({ path: ['workspace', 'ws', 'report.md'] }) + ) + + expect(response.status).toBe(499) + expect(serveLogger.error).not.toHaveBeenCalled() + }) + describe('versioned cache lifetime', () => { const principal = createExecutorPrincipal({ subjectUserId: 'test-user-id', diff --git a/apps/sim/app/api/files/serve/[...path]/route.ts b/apps/sim/app/api/files/serve/[...path]/route.ts index 478bab67a90..ec99bbfb84b 100644 --- a/apps/sim/app/api/files/serve/[...path]/route.ts +++ b/apps/sim/app/api/files/serve/[...path]/route.ts @@ -389,6 +389,7 @@ export const GET = withRouteHandler( request.method === 'HEAD' ) } catch (error) { + if (request.signal.aborted) throw error if (error instanceof InternalUnauthenticatedError) { logger.warn('Unauthorized file access attempt', { error: error.message }) return NextResponse.json( @@ -599,6 +600,7 @@ async function handleLocalFile( options.ifNoneMatch ) } catch (error) { + if (signal?.aborted) throw error logServeFailure('Error reading local file:', error) throw error } @@ -689,6 +691,7 @@ async function handleCloudProxy( options.ifNoneMatch ) } catch (error) { + if (signal?.aborted) throw error logServeFailure('Error downloading from cloud storage:', error) throw error }