mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-24 03:15:46 -06:00
fix(google): reject malformed video operation JSON (#115465)
* fix(google): reject malformed video operation JSON * fix(google): reuse shared provider JSON reader for video operations Route successful Google video operation responses through the canonical readProviderJsonResponse so fatal UTF-8 decoding and malformed-JSON wrapping stay on the shared provider contract. Keep the lenient non-OK detail path.
This commit is contained in:
@@ -705,13 +705,58 @@ describe("google video generation provider", () => {
|
||||
cfg: {},
|
||||
durationSeconds: 3,
|
||||
}),
|
||||
).rejects.toThrow("Google video operation response exceeds 16777216 bytes");
|
||||
).rejects.toThrow("Google video operation response: JSON response exceeds 16777216 bytes");
|
||||
|
||||
expect(fetchMock).toHaveBeenCalledTimes(1);
|
||||
expect(streamed.getReadCount()).toBeLessThan(64);
|
||||
expect(streamed.wasCanceled()).toBe(true);
|
||||
});
|
||||
|
||||
it("reports malformed Google REST operation JSON with a stable provider error", async () => {
|
||||
vi.spyOn(providerAuthRuntime, "resolveApiKeyForProvider").mockResolvedValue({
|
||||
apiKey: "google-key",
|
||||
source: "env",
|
||||
mode: "api-key",
|
||||
});
|
||||
generateVideosMock.mockRejectedValue(Object.assign(new Error("sdk 404"), { status: 404 }));
|
||||
vi.stubGlobal("fetch", vi.fn().mockResolvedValue(new Response("{ nope", { status: 200 })));
|
||||
|
||||
await expect(
|
||||
buildGoogleVideoGenerationProvider().generateVideo({
|
||||
provider: "google",
|
||||
model: "veo-3.1-fast-generate-preview",
|
||||
prompt: "A tiny robot watering a windowsill garden",
|
||||
cfg: {},
|
||||
durationSeconds: 3,
|
||||
}),
|
||||
).rejects.toThrow("Google video operation response: malformed JSON response");
|
||||
});
|
||||
|
||||
it("rejects invalid UTF-8 in Google REST operation JSON before parsing", async () => {
|
||||
vi.spyOn(providerAuthRuntime, "resolveApiKeyForProvider").mockResolvedValue({
|
||||
apiKey: "google-key",
|
||||
source: "env",
|
||||
mode: "api-key",
|
||||
});
|
||||
generateVideosMock.mockRejectedValue(Object.assign(new Error("sdk 404"), { status: 404 }));
|
||||
const invalidUtf8Json = new Uint8Array([
|
||||
...Buffer.from('{"done":true,"name":"operations/'),
|
||||
0xff,
|
||||
...Buffer.from('"}'),
|
||||
]);
|
||||
vi.stubGlobal("fetch", vi.fn().mockResolvedValue(new Response(invalidUtf8Json)));
|
||||
|
||||
await expect(
|
||||
buildGoogleVideoGenerationProvider().generateVideo({
|
||||
provider: "google",
|
||||
model: "veo-3.1-fast-generate-preview",
|
||||
prompt: "A tiny robot watering a windowsill garden",
|
||||
cfg: {},
|
||||
durationSeconds: 3,
|
||||
}),
|
||||
).rejects.toThrow("Google video operation response: malformed JSON response");
|
||||
});
|
||||
|
||||
it("retries transient Google REST poll failures with empty bodies", async () => {
|
||||
vi.useFakeTimers();
|
||||
vi.spyOn(providerAuthRuntime, "resolveApiKeyForProvider").mockResolvedValue({
|
||||
|
||||
@@ -4,6 +4,7 @@ import { resolveApiKeyForProvider } from "openclaw/plugin-sdk/provider-auth-runt
|
||||
import {
|
||||
createProviderOperationDeadline,
|
||||
executeProviderOperationWithRetry,
|
||||
readProviderJsonResponse,
|
||||
resolveProviderOperationTimeoutMs,
|
||||
waitProviderOperationPollInterval,
|
||||
} from "openclaw/plugin-sdk/provider-http";
|
||||
@@ -347,16 +348,13 @@ async function requestGoogleVideoJson(params: {
|
||||
signal: controller.signal,
|
||||
});
|
||||
try {
|
||||
const buffer = await readResponseWithLimit(
|
||||
response,
|
||||
GOOGLE_VIDEO_OPERATION_RESPONSE_MAX_BYTES,
|
||||
{
|
||||
onOverflow: ({ maxBytes }) =>
|
||||
new Error(`Google video operation response exceeds ${maxBytes} bytes`),
|
||||
},
|
||||
);
|
||||
const text = new TextDecoder().decode(buffer);
|
||||
if (!response.ok) {
|
||||
const text = new TextDecoder().decode(
|
||||
await readResponseWithLimit(response, GOOGLE_VIDEO_OPERATION_RESPONSE_MAX_BYTES, {
|
||||
onOverflow: ({ maxBytes }) =>
|
||||
new Error(`Google video operation response exceeds ${maxBytes} bytes`),
|
||||
}),
|
||||
);
|
||||
let detail: unknown = text;
|
||||
if (text) {
|
||||
try {
|
||||
@@ -367,8 +365,9 @@ async function requestGoogleVideoJson(params: {
|
||||
}
|
||||
throw createHttpError(response, detail);
|
||||
}
|
||||
const payload = text ? (JSON.parse(text) as unknown) : {};
|
||||
return payload;
|
||||
return await readProviderJsonResponse(response, "Google video operation response", {
|
||||
maxBytes: GOOGLE_VIDEO_OPERATION_RESPONSE_MAX_BYTES,
|
||||
});
|
||||
} finally {
|
||||
await release();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user