From 74668ea8a17e52a2191deed108d96f979a4d639d Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 21 Apr 2026 22:49:52 +0100 Subject: [PATCH] fix(image-generation): log provider fallback failures --- CHANGELOG.md | 1 + src/image-generation/runtime.test.ts | 3 +++ src/image-generation/runtime.ts | 9 ++++++++- test/helpers/media-generation/runtime-module-mocks.ts | 2 +- test/helpers/media-generation/runtime-test-mocks.ts | 2 ++ 5 files changed, 15 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3042ffd4df2..04fe9b8a413 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ Docs: https://docs.openclaw.ai ### Fixes - Agents/ACP: skip the `sessions_send` A2A ping-pong flow when a parent sends to its own background oneshot ACP child, preventing parent/child echo loops while preserving normal A2A delivery for non-parent senders. (#69817) Thanks @scotthuang. +- Image generation: log failed provider/model candidates at warn level before automatic provider fallback, so OpenAI image failures are visible in the gateway log even when a later provider succeeds. - Agents/subagents: stop terminal failed subagent runs from freezing or announcing captured reply text, so failover-exhausted runs report a clean failure instead of replaying stale assistant/tool output. - Security/external content: strip common self-hosted LLM chat-template special-token literals, including Qwen/ChatML, Llama, Gemma, Mistral, Phi, and GPT-OSS markers, from wrapped external content and metadata, preventing tokenizer-layer role-boundary spoofing against OpenAI-compatible backends that preserve special tokens in user text. - npm/install: mirror the `node-domexception` alias into root `package.json` `overrides`, so npm installs stop surfacing the deprecated `google-auth-library -> gaxios -> node-fetch -> fetch-blob -> node-domexception` chain pulled through Pi/Google runtime deps. Thanks @vincentkoc. diff --git a/src/image-generation/runtime.test.ts b/src/image-generation/runtime.test.ts index b8bffe8da65..06044caa820 100644 --- a/src/image-generation/runtime.test.ts +++ b/src/image-generation/runtime.test.ts @@ -148,6 +148,9 @@ describe("image-generation runtime", () => { error: "OpenAI API key missing", }, ]); + expect(mocks.warn).toHaveBeenCalledWith( + "image-generation candidate failed: openai/gpt-image-1: OpenAI API key missing", + ); }); it("drops unsupported provider geometry overrides and reports them", async () => { diff --git a/src/image-generation/runtime.ts b/src/image-generation/runtime.ts index f783808c346..b52a3de2e85 100644 --- a/src/image-generation/runtime.ts +++ b/src/image-generation/runtime.ts @@ -59,6 +59,9 @@ export async function generateImage( error, }); lastError = new Error(error); + log.warn( + `image-generation candidate failed: ${candidate.provider}/${candidate.model}: ${error}`, + ); continue; } @@ -112,7 +115,11 @@ export async function generateImage( status: described?.status, code: described?.code, }); - log.debug(`image-generation candidate failed: ${candidate.provider}/${candidate.model}`); + log.warn( + `image-generation candidate failed: ${candidate.provider}/${candidate.model}: ${ + described?.message ?? formatErrorMessage(err) + }`, + ); } } diff --git a/test/helpers/media-generation/runtime-module-mocks.ts b/test/helpers/media-generation/runtime-module-mocks.ts index 68cc58c4986..218cbf453eb 100644 --- a/test/helpers/media-generation/runtime-module-mocks.ts +++ b/test/helpers/media-generation/runtime-module-mocks.ts @@ -25,7 +25,7 @@ const mediaRuntimeMocks = vi.hoisted(() => { }; }; return { - createSubsystemLogger: vi.fn(() => ({ debug, warn: vi.fn() })), + createSubsystemLogger: vi.fn(() => ({ debug, warn })), describeFailoverError: vi.fn(), getImageGenerationProvider: vi.fn< (providerId: string, config?: OpenClawConfig) => ImageGenerationProvider | undefined diff --git a/test/helpers/media-generation/runtime-test-mocks.ts b/test/helpers/media-generation/runtime-test-mocks.ts index f17bd6de238..4f9bb5c1136 100644 --- a/test/helpers/media-generation/runtime-test-mocks.ts +++ b/test/helpers/media-generation/runtime-test-mocks.ts @@ -22,6 +22,7 @@ export type GenerationRuntimeMocks = { resolveAgentModelFallbackValues: ResettableReturnMock; resolveAgentModelPrimaryValue: ResettableReturnMock; debug: ResettableMock; + warn: ResettableMock; }; export function resetGenerationRuntimeMocks(mocks: GenerationRuntimeMocks): void { @@ -42,4 +43,5 @@ export function resetGenerationRuntimeMocks(mocks: GenerationRuntimeMocks): void mocks.resolveAgentModelPrimaryValue.mockReset(); mocks.resolveAgentModelPrimaryValue.mockReturnValue(undefined); mocks.debug.mockReset(); + mocks.warn.mockReset(); }