fix(ollama): bound embedding error bodies

This commit is contained in:
Vincent Koc
2026-06-19 18:02:10 +02:00
parent 70a48a680d
commit 2c8d19d73e
2 changed files with 62 additions and 1 deletions
@@ -83,6 +83,28 @@ function firstGuardedFetchCall(): Record<string, unknown> {
return call as Record<string, unknown>;
}
function cancelTrackedResponse(
text: string,
init: ResponseInit,
): {
response: Response;
wasCanceled: () => boolean;
} {
let canceled = false;
const stream = new ReadableStream<Uint8Array>({
start(controller) {
controller.enqueue(new TextEncoder().encode(text));
},
cancel() {
canceled = true;
},
});
return {
response: new Response(stream, init),
wasCanceled: () => canceled,
};
}
function expectEmbeddingFetch(
fetchMock: ReturnType<typeof mockEmbeddingFetch>,
url: string,
@@ -317,6 +339,39 @@ describe("ollama embedding provider", () => {
});
});
it("bounds embed error bodies without using response.text()", async () => {
const tracked = cancelTrackedResponse(`${"ollama embed unavailable ".repeat(1024)}tail`, {
status: 503,
headers: { "content-type": "text/plain" },
});
const textSpy = vi.spyOn(tracked.response, "text").mockRejectedValue(new Error("unbounded"));
vi.stubGlobal(
"fetch",
vi.fn(async () => tracked.response),
);
const { provider } = await createOllamaEmbeddingProvider({
config: {} as OpenClawConfig,
provider: "ollama",
model: "nomic-embed-text",
fallback: "none",
remote: { baseUrl: "http://127.0.0.1:11434" },
});
let error: unknown;
try {
await provider.embedQuery("hello");
} catch (err) {
error = err;
}
expect(String(error)).toContain("Ollama embed HTTP 503");
expect(String(error)).toContain("ollama embed unavailable");
expect(String(error)).not.toContain("tail");
expect(tracked.wasCanceled()).toBe(true);
expect(textSpy).not.toHaveBeenCalled();
});
it("reports malformed embed JSON with a provider-owned error", async () => {
vi.stubGlobal(
"fetch",
+7 -1
View File
@@ -6,6 +6,7 @@ import {
normalizeOptionalSecretInput,
} from "openclaw/plugin-sdk/provider-auth";
import { resolveEnvApiKey } from "openclaw/plugin-sdk/provider-auth-runtime";
import { readResponseTextLimited } from "openclaw/plugin-sdk/provider-http";
import { normalizeProviderId } from "openclaw/plugin-sdk/provider-model-shared";
import {
hasConfiguredSecretInput,
@@ -57,6 +58,7 @@ export type OllamaEmbeddingClient = {
type OllamaEmbeddingClientConfig = Omit<OllamaEmbeddingClient, "embedBatch">;
export const DEFAULT_OLLAMA_EMBEDDING_MODEL = "nomic-embed-text";
const OLLAMA_EMBED_ERROR_BODY_LIMIT_BYTES = 8 * 1024;
const QUERY_INSTRUCTION_TEMPLATES = [
{
@@ -340,7 +342,11 @@ export async function createOllamaEmbeddingProvider(
},
onResponse: async (response) => {
if (!response.ok) {
throw new Error(`Ollama embed HTTP ${response.status}: ${await response.text()}`);
const detail = await readResponseTextLimited(
response,
OLLAMA_EMBED_ERROR_BODY_LIMIT_BYTES,
).catch(() => "unknown error");
throw new Error(`Ollama embed HTTP ${response.status}: ${detail}`);
}
return await readOllamaEmbeddingJsonResponse(response);
},