Skip to content

fix: classify OpenRouter 'no endpoints found that support tool use' 404 as model_not_found with fallback - #58451

Closed
webtecnica wants to merge 2 commits into
NousResearch:mainfrom
webtecnica:fix/openrouter-tool-use-404-fallback
Closed

fix: classify OpenRouter 'no endpoints found that support tool use' 404 as model_not_found with fallback#58451
webtecnica wants to merge 2 commits into
NousResearch:mainfrom
webtecnica:fix/openrouter-tool-use-404-fallback

Conversation

@webtecnica

Copy link
Copy Markdown
Contributor

Problem

When OpenRouter routes to an endpoint that does not support tool/function calling, it returns HTTP 404:

No endpoints found that support tool use. Try disabling "browser_back".
To learn more about provider routing, visit:
https://openrouter.ai/docs/guides/routing/provider-selection

The raw error body does not contain "model not found" or any other _MODEL_NOT_FOUND_PATTERNS entry, so it falls through to FailoverReason.unknown with retryable=True. The retry loop wastes 3–5 attempts on the same deterministic rejection, then surfaces a confusing generic error instead of automatically failing over to a fallback model or provider.

Fix

Added the OpenRouter phrase "no endpoints found that support tool use" to _MODEL_NOT_FOUND_PATTERNS in agent/error_classifier.py.

This classifies the error as model_not_found (retryable=False, should_fallback=True), which triggers the client-error fast-fallback path in conversation_loop.py: the agent switches to a configured fallback model/provider before the user ever sees the error.

What doesn't change

  • Existing buffered guidance in conversation_loop.py (the "support tool use" hint at line ~2967) remains intact — it surfaces only if every fallback exhausts.
  • The _PROVIDER_POLICY_BLOCKED_PATTERNS are unaffected — those are distinct OpenRouter errors about data/privacy guardrails where fallback would not help.

Testing

  • ✅ Pattern matches the exact OpenRouter error body (confirmed from user reports)
  • ✅ Classifier returns model_not_foundretryable=False, should_fallback=True
  • ✅ This triggers is_client_error_try_activate_fallback() in the retry loop
  • ✅ The existing "support tool use" hint at conversation_loop.py:2967 still fires as a fallback safeguard

@webtecnica
webtecnica requested a review from a team July 4, 2026 18:52
@alt-glitch alt-glitch added type/bug Something isn't working comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint provider/openrouter OpenRouter aggregator P2 Medium — degraded but workaround exists labels Jul 4, 2026
teknium1 added a commit that referenced this pull request Jul 4, 2026
- package-lock.json changes in #58451 were unrelated peer-flag churn
- CANONICAL_PROVIDERS 'poolside' entry from #58374 has no ProviderConfig
  in hermes_cli/auth.py and no setup flow, so the picker entry would be
  dead; the wire-format coercions stand on their own
…llback

When OpenRouter routes to an endpoint that does not support tool/function
calling, it returns HTTP 404 with the message 'No endpoints found that
support tool use. Try disabling "browser_back".'

The raw error body does not contain 'model not found' or any other
_MODEL_NOT_FOUND_PATTERNS entry, so it falls through to FailoverReason.unknown
with retryable=True. The retry loop wastes 3-5 attempts on the same
deterministic rejection, then surfaces a confusing generic error instead of
automatically failing over to a fallback model or provider.

Adding the OpenRouter phrase to _MODEL_NOT_FOUND_PATTERNS classifies it as
model_not_found (retryable=False, should_fallback=True), which triggers the
client-error fast-fallback path in conversation_loop.py: the agent switches
to a configured fallback model/provider before the user sees the error.

Existing buffered guidance in conversation_loop.py (the 'support tool use'
hint at line ~2967) remains intact and surfaces only if every fallback
exhausts.
@webtecnica
webtecnica force-pushed the fix/openrouter-tool-use-404-fallback branch from 9f92ab6 to 48590ca Compare July 4, 2026 22:33
@webtecnica

Copy link
Copy Markdown
Contributor Author

Update: diff limpo + teste de regressão

Rebaseei no main mais recente e fiz duas melhorias:

  1. Removi o package-lock.json noise — o diff agora tem só 2 arquivos, +24 linhas (apenas error_classifier.py + teste).
  2. Adicionei teste de regressãotest_404_openrouter_tool_use_not_supported em tests/agent/test_error_classifier.py. Verifica que a mensagem exata do OpenRouter classifica como model_not_found com retryable=False, should_fallback=True.

Todos os 180 testes do error_classifier passam. 👌

teknium1 added a commit that referenced this pull request Jul 4, 2026
- package-lock.json changes in #58451 were unrelated peer-flag churn
- CANONICAL_PROVIDERS 'poolside' entry from #58374 has no ProviderConfig
  in hermes_cli/auth.py and no setup flow, so the picker entry would be
  dead; the wire-format coercions stand on their own
teknium1 added a commit that referenced this pull request Jul 4, 2026
- package-lock.json changes in #58451 were unrelated peer-flag churn
- CANONICAL_PROVIDERS 'poolside' entry from #58374 has no ProviderConfig
  in hermes_cli/auth.py and no setup flow, so the picker entry would be
  dead; the wire-format coercions stand on their own
habarmc1223-sudo pushed a commit to habarmc1223-sudo/hermes-agent-fluxmem that referenced this pull request Jul 8, 2026
- package-lock.json changes in NousResearch#58451 were unrelated peer-flag churn
- CANONICAL_PROVIDERS 'poolside' entry from NousResearch#58374 has no ProviderConfig
  in hermes_cli/auth.py and no setup flow, so the picker entry would be
  dead; the wire-format coercions stand on their own
santhreal pushed a commit to santhreal/hermes-agent that referenced this pull request Jul 13, 2026
- package-lock.json changes in NousResearch#58451 were unrelated peer-flag churn
- CANONICAL_PROVIDERS 'poolside' entry from NousResearch#58374 has no ProviderConfig
  in hermes_cli/auth.py and no setup flow, so the picker entry would be
  dead; the wire-format coercions stand on their own
Gravezzz pushed a commit to Gravezzz/hermes-agent that referenced this pull request Jul 21, 2026
- package-lock.json changes in NousResearch#58451 were unrelated peer-flag churn
- CANONICAL_PROVIDERS 'poolside' entry from NousResearch#58374 has no ProviderConfig
  in hermes_cli/auth.py and no setup flow, so the picker entry would be
  dead; the wire-format coercions stand on their own
leewenjie pushed a commit to leewenjie/hermes-agent that referenced this pull request Aug 7, 2026
- package-lock.json changes in NousResearch#58451 were unrelated peer-flag churn
- CANONICAL_PROVIDERS 'poolside' entry from NousResearch#58374 has no ProviderConfig
  in hermes_cli/auth.py and no setup flow, so the picker entry would be
  dead; the wire-format coercions stand on their own
webdevtodayjason added a commit to webdevtodayjason/hermes-agent that referenced this pull request Aug 13, 2026
…n error quirks

Adds a plugin seam at the top of agent/error_classifier.classify_api_error()
(step 0, before the built-in pipeline) so model-provider plugins can classify
their provider's error quirks without patching core:

- New "classify_api_error" entry in VALID_HOOKS. Callbacks receive the parsed
  error context (provider, model, status_code, error_type, error_code,
  error_message, error_body, error, approx_tokens, context_length,
  num_messages), self-scope on `provider`, and return None to pass or a dict
  {"reason": "<FailoverReason name>", ...optional recovery-hint overrides}.
- get_plugin_error_classification() helper mirrors
  get_pre_tool_call_block_message(): first valid result wins, invalid dicts
  and unknown reasons are skipped, callback exceptions are isolated — a
  broken plugin can never break classification. Zero behavior change when no
  plugin claims the error (all 179 existing classifier tests pass untouched).
- Bundled reference plugin `openrouter-tool-use-404` (opt-in, like all
  bundled standalone plugins) re-implements PR NousResearch#58451: OpenRouter's
  "No endpoints found that support tool use" 404 carries no
  _MODEL_NOT_FOUND_PATTERNS signal, so it classifies as unknown/retryable
  and the retry loop burns 3-5 attempts on a deterministic rejection.
  The plugin classifies it as model_not_found (retryable=False,
  should_fallback=True) so the fast-fallback path fires immediately —
  demonstrating a waiting core PR converted to a publishable plugin.

Motivation: ~10 open PRs are single-provider error-classification patches
(NousResearch#58451, NousResearch#58355, NousResearch#58502, NousResearch#58474, NousResearch#58366, ...). This hook turns that whole
class of contribution into plugin territory.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FWMcB7RPSYUpsXDfBgwjzM
teknium1 pushed a commit that referenced this pull request Aug 13, 2026
…n error quirks

Adds a plugin seam at the top of agent/error_classifier.classify_api_error()
(step 0, before the built-in pipeline) so model-provider plugins can classify
their provider's error quirks without patching core:

- New "classify_api_error" entry in VALID_HOOKS. Callbacks receive the parsed
  error context (provider, model, status_code, error_type, error_code,
  error_message, error_body, error, approx_tokens, context_length,
  num_messages), self-scope on `provider`, and return None to pass or a dict
  {"reason": "<FailoverReason name>", ...optional recovery-hint overrides}.
- get_plugin_error_classification() helper mirrors
  get_pre_tool_call_block_message(): first valid result wins, invalid dicts
  and unknown reasons are skipped, callback exceptions are isolated — a
  broken plugin can never break classification. Zero behavior change when no
  plugin claims the error (all 179 existing classifier tests pass untouched).
- Bundled reference plugin `openrouter-tool-use-404` (opt-in, like all
  bundled standalone plugins) re-implements PR #58451: OpenRouter's
  "No endpoints found that support tool use" 404 carries no
  _MODEL_NOT_FOUND_PATTERNS signal, so it classifies as unknown/retryable
  and the retry loop burns 3-5 attempts on a deterministic rejection.
  The plugin classifies it as model_not_found (retryable=False,
  should_fallback=True) so the fast-fallback path fires immediately —
  demonstrating a waiting core PR converted to a publishable plugin.

Motivation: ~10 open PRs are single-provider error-classification patches
(#58451, #58355, #58502, #58474, #58366, ...). This hook turns that whole
class of contribution into plugin territory.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FWMcB7RPSYUpsXDfBgwjzM
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P2 Medium — degraded but workaround exists provider/openrouter OpenRouter aggregator type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants