diff --git a/src/agents/models-config.providers.ts b/src/agents/models-config.providers.ts index 3662ce9a3..4f921b6dd 100644 --- a/src/agents/models-config.providers.ts +++ b/src/agents/models-config.providers.ts @@ -685,6 +685,12 @@ function buildOpenrouterProvider(): ProviderConfig { { id: OPENROUTER_DEFAULT_MODEL_ID, name: "OpenRouter Auto", + // reasoning: false here is a catalog default only; it does NOT cause + // `reasoning.effort: "none"` to be sent for the "auto" routing model. + // applyExtraParamsToAgent skips the reasoning effort injection for + // model id "auto" because it dynamically routes to any OpenRouter model + // (including ones where reasoning is mandatory and cannot be disabled). + // See: openclaw/openclaw#24851 reasoning: false, input: ["text", "image"], cost: OPENROUTER_DEFAULT_COST, diff --git a/src/agents/pi-embedded-runner/extra-params.ts b/src/agents/pi-embedded-runner/extra-params.ts index 66b077af2..0d88bdf08 100644 --- a/src/agents/pi-embedded-runner/extra-params.ts +++ b/src/agents/pi-embedded-runner/extra-params.ts @@ -546,7 +546,14 @@ export function applyExtraParamsToAgent( if (provider === "openrouter") { log.debug(`applying OpenRouter app attribution headers for ${provider}/${modelId}`); - agent.streamFn = createOpenRouterWrapper(agent.streamFn, thinkingLevel); + // "auto" is a dynamic routing model — we don't know which underlying model + // OpenRouter will select, and it may be a reasoning-required endpoint. + // Omit the thinkingLevel so we never inject `reasoning.effort: "none"`, + // which would cause a 400 on models where reasoning is mandatory. + // Users who need reasoning control should target a specific model ID. + // See: openclaw/openclaw#24851 + const openRouterThinkingLevel = modelId === "auto" ? undefined : thinkingLevel; + agent.streamFn = createOpenRouterWrapper(agent.streamFn, openRouterThinkingLevel); agent.streamFn = createOpenRouterSystemCacheWrapper(agent.streamFn); }