diff --git a/src/gateway/r2.test.ts b/src/gateway/r2.test.ts index 83b03ae43..e6d3a55b4 100644 --- a/src/gateway/r2.test.ts +++ b/src/gateway/r2.test.ts @@ -142,6 +142,25 @@ describe('mountR2Storage', () => { expect(console.error).toHaveBeenCalledWith('Failed to mount R2 bucket:', expect.any(Error)); }); + it('returns true when mount fails with "already in use" error', async () => { + const { sandbox, mountBucketMock, startProcessMock } = createMockSandbox({ mounted: false }); + mountBucketMock.mockRejectedValue( + new Error('InvalidMountConfigError: Mount path "/data/moltbot" is already in use by bucket "moltbot-data". Unmount the existing bucket first or use a different mount path.'), + ); + startProcessMock.mockResolvedValueOnce(createMockProcess('')); + + const env = createMockEnvWithR2(); + + const result = await mountR2Storage(sandbox, env); + + expect(result).toBe(true); + expect(console.log).toHaveBeenCalledWith( + 'R2 bucket already mounted at', + '/data/moltbot', + '(detected via mount error)', + ); + }); + it('returns true if mount fails but check shows it is actually mounted', async () => { const { sandbox, mountBucketMock, startProcessMock } = createMockSandbox(); startProcessMock diff --git a/src/gateway/r2.ts b/src/gateway/r2.ts index c95efc40b..124dabe11 100644 --- a/src/gateway/r2.ts +++ b/src/gateway/r2.ts @@ -65,6 +65,12 @@ export async function mountR2Storage(sandbox: Sandbox, env: MoltbotEnv): Promise const errorMessage = err instanceof Error ? err.message : String(err); console.log('R2 mount error:', errorMessage); + // "already in use" means the bucket is mounted — treat as success + if (errorMessage.includes('already in use')) { + console.log('R2 bucket already mounted at', R2_MOUNT_PATH, '(detected via mount error)'); + return true; + } + // Check again if it's mounted - the error might be misleading if (await isR2Mounted(sandbox)) { console.log('R2 bucket is mounted despite error'); diff --git a/src/gateway/sync.test.ts b/src/gateway/sync.test.ts index f062ffed7..b8a89aaef 100644 --- a/src/gateway/sync.test.ts +++ b/src/gateway/sync.test.ts @@ -1,4 +1,4 @@ -import { describe, it, expect, beforeEach } from 'vitest'; +import { describe, it, expect, beforeEach, vi } from 'vitest'; import { syncToR2 } from './sync'; import { createMockEnv, @@ -8,6 +8,10 @@ import { suppressConsole, } from '../test-utils'; +vi.mock('./process', () => ({ + ensureMoltbotGateway: vi.fn().mockResolvedValue({ id: 'mock-process' }), +})); + describe('syncToR2', () => { beforeEach(() => { suppressConsole(); diff --git a/src/gateway/sync.ts b/src/gateway/sync.ts index 63808c471..cfc4c48de 100644 --- a/src/gateway/sync.ts +++ b/src/gateway/sync.ts @@ -1,6 +1,7 @@ import type { Sandbox } from '@cloudflare/sandbox'; import type { MoltbotEnv } from '../types'; import { R2_MOUNT_PATH } from '../config'; +import { ensureMoltbotGateway } from './process'; import { mountR2Storage } from './r2'; import { waitForProcess } from './utils'; @@ -35,6 +36,18 @@ export async function syncToR2(sandbox: Sandbox, env: MoltbotEnv): Promise