diff --git a/src/agents/pi-embedded-runner/model.test.ts b/src/agents/pi-embedded-runner/model.test.ts index 5debe686e8..dcbf92380e 100644 --- a/src/agents/pi-embedded-runner/model.test.ts +++ b/src/agents/pi-embedded-runner/model.test.ts @@ -340,4 +340,29 @@ describe("resolveModel", () => { }, }); }); + + it("includes auth hint for unknown ollama models (#17328)", () => { + // resetMockDiscoverModels() in beforeEach already sets find → null + const result = resolveModel("ollama", "gemma3:4b", "/tmp/agent"); + + expect(result.model).toBeUndefined(); + expect(result.error).toContain("Unknown model: ollama/gemma3:4b"); + expect(result.error).toContain("OLLAMA_API_KEY"); + expect(result.error).toContain("docs.openclaw.ai/providers/ollama"); + }); + + it("includes auth hint for unknown vllm models", () => { + const result = resolveModel("vllm", "llama-3-70b", "/tmp/agent"); + + expect(result.model).toBeUndefined(); + expect(result.error).toContain("Unknown model: vllm/llama-3-70b"); + expect(result.error).toContain("VLLM_API_KEY"); + }); + + it("does not add auth hint for non-local providers", () => { + const result = resolveModel("google-antigravity", "some-model", "/tmp/agent"); + + expect(result.model).toBeUndefined(); + expect(result.error).toBe("Unknown model: google-antigravity/some-model"); + }); }); diff --git a/src/agents/pi-embedded-runner/model.ts b/src/agents/pi-embedded-runner/model.ts index 247600a58e..3eb8191744 100644 --- a/src/agents/pi-embedded-runner/model.ts +++ b/src/agents/pi-embedded-runner/model.ts @@ -93,10 +93,38 @@ export function resolveModel( return { model: fallbackModel, authStorage, modelRegistry }; } return { - error: `Unknown model: ${provider}/${modelId}`, + error: buildUnknownModelError(provider, modelId), authStorage, modelRegistry, }; } return { model: normalizeModelCompat(model), authStorage, modelRegistry }; } + +/** + * Build a more helpful error when the model is not found. + * + * Local providers (ollama, vllm) need a dummy API key to be registered. + * Users often configure `agents.defaults.model.primary: "ollama/…"` but + * forget to set `OLLAMA_API_KEY`, resulting in a confusing "Unknown model" + * error. This detects known providers that require opt-in auth and adds + * a hint. + * + * See: https://github.com/openclaw/openclaw/issues/17328 + */ +const LOCAL_PROVIDER_HINTS: Record = { + ollama: + "Ollama requires authentication to be registered as a provider. " + + 'Set OLLAMA_API_KEY="ollama-local" (any value works) or run "openclaw configure". ' + + "See: https://docs.openclaw.ai/providers/ollama", + vllm: + "vLLM requires authentication to be registered as a provider. " + + 'Set VLLM_API_KEY (any value works) or run "openclaw configure". ' + + "See: https://docs.openclaw.ai/providers/vllm", +}; + +function buildUnknownModelError(provider: string, modelId: string): string { + const base = `Unknown model: ${provider}/${modelId}`; + const hint = LOCAL_PROVIDER_HINTS[provider.toLowerCase()]; + return hint ? `${base}. ${hint}` : base; +}