From 50242b4ee95001745c0495579d507f6852f38e0a Mon Sep 17 00:00:00 2001 From: Hannes Rudolph Date: Fri, 24 Oct 2025 17:52:57 -0600 Subject: [PATCH] fix(openai-native): add logging for background resume and polling; classify permanent vs transient errors; chore(task): remove temporary debug log --- src/api/providers/openai-native.ts | 32 ++++++++++++++++++++++++++---- src/core/task/Task.ts | 1 - 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/api/providers/openai-native.ts b/src/api/providers/openai-native.ts index 15e6255c18..4eb449c63d 100644 --- a/src/api/providers/openai-native.ts +++ b/src/api/providers/openai-native.ts @@ -1230,13 +1230,18 @@ export class OpenAiNativeHandler extends BaseProvider implements SingleCompletio this.resumeCutoffSequence = undefined throw e } - } catch (err) { + } catch (err: any) { // If terminal error, don't keep retrying resume; fall back to polling immediately const delay = resumeBaseDelayMs * Math.pow(2, attempt) + const msg = err instanceof Error ? err.message : String(err) + if (isTerminalBackgroundError(err)) { + console.error(`[OpenAiNative][resume] terminal background error on attempt ${attempt + 1}: ${msg}`) break } - // Otherwise retry with backoff + + // Otherwise retry with backoff (transient failure) + console.warn(`[OpenAiNative][resume] attempt ${attempt + 1} failed; retrying in ${delay}ms: ${msg}`) if (delay > 0) { await new Promise((r) => setTimeout(r, delay)) } @@ -1354,12 +1359,31 @@ export class OpenAiNativeHandler extends BaseProvider implements SingleCompletio const msg = detail ? `Response ${status}: ${detail}` : `Response ${status}: ${respId || responseId}` throw createTerminalBackgroundError(msg) } - } catch (err) { + } catch (err: any) { // If we've already emitted a terminal status, propagate to consumer to stop polling. if (lastEmittedStatus === "failed" || lastEmittedStatus === "canceled") { throw err } - // Otherwise ignore transient poll errors + + // Classify polling errors and log appropriately + const statusCode = err?.status ?? err?.response?.status + const msg = err instanceof Error ? err.message : String(err) + + // Permanent errors: stop polling + if (statusCode === 401 || statusCode === 403 || statusCode === 404) { + console.error(`[OpenAiNative][poll] permanent error (status ${statusCode}); stopping: ${msg}`) + throw createTerminalBackgroundError(`Polling failed with status ${statusCode}: ${msg}`) + } + + // Rate limit: transient, will retry + if (statusCode === 429) { + console.warn(`[OpenAiNative][poll] rate limited; will retry: ${msg}`) + } else { + // Other transient/network errors + console.warn( + `[OpenAiNative][poll] transient error; will retry${statusCode ? ` (status ${statusCode})` : ""}: ${msg}`, + ) + } } // Stop polling immediately on terminal background statuses diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index 66ebffe699..a88fc32ec8 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -2351,7 +2351,6 @@ export class Task extends EventEmitter implements TaskLike { // lastMessage.ts = Date.now() DO NOT update ts since it is used as a key for virtuoso list lastMessage.partial = false // instead of streaming partialMessage events, we do a save and post like normal to persist to disk - console.log("updating partial message", lastMessage) } // Update `api_req_started` to have cancelled and cost, so that