From 6a4add2011f41658a83382451f8791c091b3f4d7 Mon Sep 17 00:00:00 2001 From: justsisyphus Date: Tue, 20 Jan 2026 14:37:10 +0900 Subject: [PATCH] fix(cli/run): add retry mechanism for session creation The OpenCode server may not be fully initialized even after reporting 'listening'. Add retry with exponential backoff (3 attempts) and proper error handling to make session creation more robust. This fixes CI failures where session.create() fails immediately after server startup. Fixes #935 (CI failure) --- src/cli/run/runner.ts | 48 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/src/cli/run/runner.ts b/src/cli/run/runner.ts index a648417af..30e46688f 100644 --- a/src/cli/run/runner.ts +++ b/src/cli/run/runner.ts @@ -6,6 +6,8 @@ import { createEventState, processEvents, serializeError } from "./events" const POLL_INTERVAL_MS = 500 const DEFAULT_TIMEOUT_MS = 0 +const SESSION_CREATE_MAX_RETRIES = 3 +const SESSION_CREATE_RETRY_DELAY_MS = 1000 export async function run(options: RunOptions): Promise { const { @@ -45,13 +47,49 @@ export async function run(options: RunOptions): Promise { }) try { - const sessionRes = await client.session.create({ - body: { title: "oh-my-opencode run" }, - }) + // Retry session creation with exponential backoff + // Server might not be fully ready even after "listening" message + let sessionID: string | undefined + let lastError: unknown + + for (let attempt = 1; attempt <= SESSION_CREATE_MAX_RETRIES; attempt++) { + const sessionRes = await client.session.create({ + body: { title: "oh-my-opencode run" }, + }) + + if (sessionRes.error) { + lastError = sessionRes.error + console.error(pc.yellow(`Session create attempt ${attempt}/${SESSION_CREATE_MAX_RETRIES} failed:`)) + console.error(pc.dim(` Error: ${serializeError(sessionRes.error)}`)) + + if (attempt < SESSION_CREATE_MAX_RETRIES) { + const delay = SESSION_CREATE_RETRY_DELAY_MS * attempt + console.log(pc.dim(` Retrying in ${delay}ms...`)) + await new Promise((resolve) => setTimeout(resolve, delay)) + continue + } + } + + sessionID = sessionRes.data?.id + if (sessionID) { + break + } + + // No error but also no session ID - unexpected response + lastError = new Error(`Unexpected response: ${JSON.stringify(sessionRes, null, 2)}`) + console.error(pc.yellow(`Session create attempt ${attempt}/${SESSION_CREATE_MAX_RETRIES}: No session ID returned`)) + + if (attempt < SESSION_CREATE_MAX_RETRIES) { + const delay = SESSION_CREATE_RETRY_DELAY_MS * attempt + console.log(pc.dim(` Retrying in ${delay}ms...`)) + await new Promise((resolve) => setTimeout(resolve, delay)) + } + } - const sessionID = sessionRes.data?.id if (!sessionID) { - console.error(pc.red("Failed to create session")) + console.error(pc.red("Failed to create session after all retries")) + console.error(pc.dim(`Last error: ${serializeError(lastError)}`)) + cleanup() return 1 }