fix(image-generation): log provider fallback failures

This commit is contained in:
Peter Steinberger
2026-04-21 22:49:52 +01:00
parent b5c4aaf2a7
commit 74668ea8a1
5 changed files with 15 additions and 2 deletions

View File

@@ -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.

View File

@@ -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 () => {

View File

@@ -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)
}`,
);
}
}

View File

@@ -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

View File

@@ -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();
}