From 21093528a739839351cb27f307a21b3595ff222d Mon Sep 17 00:00:00 2001 From: Anand Chowdhary Date: Sun, 8 Feb 2026 17:59:07 +0100 Subject: [PATCH 1/2] Handle "already in use" error when R2 bucket is already mounted The isR2Mounted() check (which runs `mount | grep` inside the sandbox) is unreliable and can return false even when the bucket is already mounted. This causes mountBucket() to be called again on subsequent requests, throwing InvalidMountConfigError. Rather than relying solely on the unreliable mount check in the catch block, detect the "already in use" error message and treat it as a successful mount. Co-Authored-By: Claude Opus 4.6 --- src/gateway/r2.test.ts | 19 +++++++++++++++++++ src/gateway/r2.ts | 6 ++++++ 2 files changed, 25 insertions(+) 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'); From 51aeceebbf829b12f5a22d40a436fb30687ac87d Mon Sep 17 00:00:00 2001 From: Anand Chowdhary Date: Sun, 8 Feb 2026 18:40:23 +0100 Subject: [PATCH 2/2] Ensure gateway is running before syncing to R2 syncToR2 only mounted R2 but never started the gateway. On a fresh container, openclaw onboard (which creates openclaw.json) only runs during gateway startup. If "Sync now" is triggered before any other route starts the gateway, the config file doesn't exist and sync fails with "no config file found". Call ensureMoltbotGateway before syncing. Co-Authored-By: Claude Opus 4.6 --- src/gateway/sync.test.ts | 6 +++++- src/gateway/sync.ts | 13 +++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) 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