{"record":{"id":"d7d3bd20659c647a","repo":"can1357/oh-my-pi","slug":"gateway-already-attached-to-this-name","errorCode":null,"errorMessage":"Gateway already attached to ${this.name}","messagePattern":"Gateway already attached to (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/metaharness/src/tb/vmon.ts","lineNumber":302,"sourceCode":"\t\t\t}\n\t\t}\n\t\tprocess.stdin.close();\n\t\treturn captureProcess(process);\n\t}\n\n\t/** Read one guest file, returning null when it does not exist. */\n\tasync readFile(guestPath: string): Promise<string | null> {\n\t\tconst result = await this.exec(\n\t\t\t`if [ -e ${quoteGuestShell(guestPath)} ]; then cat -- ${quoteGuestShell(guestPath)}; else exit 44; fi`,\n\t\t);\n\t\tif (result.exitCode === 44) return null;\n\t\tif (result.exitCode !== 0) throw commandError(`Reading ${this.name}:${guestPath}`, result);\n\t\treturn result.stdout;\n\t}\n\n\t/** Attach the sandbox host gateway to a local HTTP(S) target. */\n\tasync startGateway(localUrl: string): Promise<string> {\n\t\tif (this.#gateway) throw new Error(`Gateway already attached to ${this.name}`);\n\t\tconst pathname = new URL(localUrl).pathname.replace(/\\/+$/, \"\");\n\t\tconst gateway = await this.#sandbox.hostGateway(localUrl);\n\t\tthis.#gateway = gateway;\n\t\treturn `${gateway.url.replace(/\\/+$/, \"\")}${pathname}`;\n\t}\n\n\t/** Build an RpcClient launcher backed by a streaming Vibemon exec. */\n\trpcTransport(entrypoint: string, timeoutSec: number): (agentArgs: string[]) => Promise<RpcAgentProcess> {\n\t\treturn async agentArgs => {\n\t\t\tconst process = await this.#sandbox.exec([entrypoint, ...agentArgs], {\n\t\t\t\tenv: { ...this.#env, HOME: this.home },\n\t\t\t\tworkdir: this.workdir,\n\t\t\t\ttimeout: timeoutSec,\n\t\t\t});\n\t\t\treturn adaptRpcProcess(process);\n\t\t};\n\t}\n","sourceCodeStart":284,"sourceCodeEnd":320,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/metaharness/src/tb/vmon.ts#L284-L320","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Call startGateway once per sandbox instance and reuse the returned URL","Create a fresh sandbox instance if you need to re-attach the gateway","Track attachment in your orchestration layer and guard repeated calls (this error is that guard surfacing)"],"exampleFix":"// before\nawait vm.startGateway(url);\nawait vm.startGateway(url); // throws\n// after\nconst gatewayUrl = gatewayUrlCache.get(vm) ?? await vm.startGateway(url);","handlingStrategy":"validation","validationCode":"let gatewayUrl: string | undefined;\nasync function ensureGateway(vm: VibemonSandbox, localUrl: string): Promise<string> {\n  return (gatewayUrl ??= await vm.startGateway(localUrl));\n}","typeGuard":null,"tryCatchPattern":"try {\n  return await vm.startGateway(localUrl);\n} catch (err) {\n  if (String(err.message).startsWith(\"Gateway already attached to\")) {\n    return retrieveExistingGatewayUrl(vm); // reuse instead of failing\n  }\n  throw err;\n}","preventionTips":["Treat each sandbox as single-use: one gateway attachment per instance","Cache the gateway URL at setup time and pass it down instead of re-calling","Recreate the sandbox when a new gateway target is needed"],"tags":["vm","gateway","state"],"backgroundTag":"already-initialized","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}