can1357/oh-my-pi · error

Gateway already attached to ${this.name}

Error message

Gateway already attached to ${this.name}

What it means

VibemonSandbox.startGateway attaches a single host-gateway forwarding the sandbox to a local HTTP(S) target. The sandbox caches the gateway in #gateway and throws this error if startGateway is called twice on the same instance, since only one gateway attachment is supported per sandbox.

Source

Thrown at packages/metaharness/src/tb/vmon.ts:302

			}
		}
		process.stdin.close();
		return captureProcess(process);
	}

	/** Read one guest file, returning null when it does not exist. */
	async readFile(guestPath: string): Promise<string | null> {
		const result = await this.exec(
			`if [ -e ${quoteGuestShell(guestPath)} ]; then cat -- ${quoteGuestShell(guestPath)}; else exit 44; fi`,
		);
		if (result.exitCode === 44) return null;
		if (result.exitCode !== 0) throw commandError(`Reading ${this.name}:${guestPath}`, result);
		return result.stdout;
	}

	/** Attach the sandbox host gateway to a local HTTP(S) target. */
	async startGateway(localUrl: string): Promise<string> {
		if (this.#gateway) throw new Error(`Gateway already attached to ${this.name}`);
		const pathname = new URL(localUrl).pathname.replace(/\/+$/, "");
		const gateway = await this.#sandbox.hostGateway(localUrl);
		this.#gateway = gateway;
		return `${gateway.url.replace(/\/+$/, "")}${pathname}`;
	}

	/** Build an RpcClient launcher backed by a streaming Vibemon exec. */
	rpcTransport(entrypoint: string, timeoutSec: number): (agentArgs: string[]) => Promise<RpcAgentProcess> {
		return async agentArgs => {
			const process = await this.#sandbox.exec([entrypoint, ...agentArgs], {
				env: { ...this.#env, HOME: this.home },
				workdir: this.workdir,
				timeout: timeoutSec,
			});
			return adaptRpcProcess(process);
		};
	}

View on GitHub (pinned to 9690622007)

Solutions

  1. Call startGateway once per sandbox instance and reuse the returned URL
  2. Create a fresh sandbox instance if you need to re-attach the gateway
  3. Track attachment in your orchestration layer and guard repeated calls (this error is that guard surfacing)

Example fix

// before
await vm.startGateway(url);
await vm.startGateway(url); // throws
// after
const gatewayUrl = gatewayUrlCache.get(vm) ?? await vm.startGateway(url);
Defensive patterns

Strategy: validation

Validate before calling

let gatewayUrl: string | undefined;
async function ensureGateway(vm: VibemonSandbox, localUrl: string): Promise<string> {
  return (gatewayUrl ??= await vm.startGateway(localUrl));
}

Try / catch

try {
  return await vm.startGateway(localUrl);
} catch (err) {
  if (String(err.message).startsWith("Gateway already attached to")) {
    return retrieveExistingGatewayUrl(vm); // reuse instead of failing
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling startGateway a second time on the same VibemonSandbox instance — e.g. runTrial invoked twice against one VM, or retry logic re-calling startGateway after a partial failure.

Common situations: Reusing a cached sandbox object across trial runs; retry wrappers that don't recreate the sandbox; calling startGateway in both setup code and the trial runner.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/d7d3bd20659c647a. Report an issue: GitHub.