hermes - ✅(Solved) Fix Auxiliary title generation 404 on custom Anthropic-mode providers (URL double-/v1) [1 pull requests, 2 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
NousResearch/hermes-agent#19753Fetched 2026-05-05 06:05:21
View on GitHub
Comments
2
Participants
2
Timeline
6
Reactions
0
Author
Participants
Timeline (top)
labeled ×3commented ×2cross-referenced ×1

When model.provider is a custom:<name> with api_mode: anthropic_messages and a base URL ending in /coding (e.g. Kimi Coding Plan), auxiliary tasks (title generation, compression, etc.) fail with HTTP 404 because the Anthropic SDK receives a base URL with /v1 already appended, then adds its own /v1/messages path — producing /coding/v1/v1/messages.

Error Message

⚠️ API call failed (attempt 1/3): NotFoundError [HTTP 404] 🔌 Provider: kimi-coding Model: kimi-for-coding 🌐 Endpoint: https://api.kimi.com/coding/v1 📝 Error: HTTP 404: The requested resource was not found

Root Cause

In resolve_provider_client() (auxiliary_client.py), the anonymous custom provider branch with explicit_base_url:

# Line ~2118
custom_base = _to_openai_base_url(explicit_base_url).strip()
# explicit_base_url = "https://api.kimi.com/coding"
# custom_base       = "https://api.kimi.com/coding/v1"  ← rewritten for OpenAI SDK

# Line ~2143-2145
client = OpenAI(api_key=custom_key, base_url=_clean_base, **extra)
client = _wrap_if_needed(client, final_model, custom_base, custom_key)
#                                                ^^^^^^^^^^^
#   Passes /coding/v1 to _maybe_wrap_anthropic → build_anthropic_client

_maybe_wrap_anthropic() detects the Kimi endpoint and wraps the client in AnthropicAuxiliaryClient via build_anthropic_client(api_key, base_url="https://api.kimi.com/coding/v1").

The Anthropic SDK constructs the request URL as:

base_url.raw_path + "/v1/messages".lstrip("/")
= /coding/v1/ + v1/messages
= /coding/v1/v1/messages  ← 404

Correct behavior: build_anthropic_client should receive https://api.kimi.com/coding (raw URL), producing /coding/v1/messages.

Fix Action

Fixed

PR fix notes

PR #19772: fix(auxiliary): pass raw explicit_base_url to _wrap_if_needed for Anthropic transport

Description (problem / solution / changelog)

Summary

Fixes #19753 — auxiliary title generation (and other aux tasks) returns HTTP 404 when using a custom:<name> provider with api_mode: anthropic_messages and an explicit_base_url.

Root Cause

In resolve_provider_client(), the explicit_base_url branch (line ~2206) passes custom_base — the URL rewritten by _to_openai_base_url() with /v1 appended — to _wrap_if_needed(). When _maybe_wrap_anthropic() forwards this to build_anthropic_client(), the Anthropic SDK appends its own /v1/messages, producing /v1/v1/messages → 404.

PR #17467 previously fixed this same class of bug for the named-custom-provider and _try_custom_endpoint branches, but missed the explicit_base_url branch.

Fix

One-line change: pass explicit_base_url (the raw, un-rewritten URL) instead of custom_base to _wrap_if_needed(), so the Anthropic SDK constructs the correct path.

Testing

  • Added regression test tests/agent/test_auxiliary_explicit_base_url_anthropic.py that verifies the source passes explicit_base_url (not custom_base) to _wrap_if_needed.
  • All existing tests pass.

Scope

  • agent/auxiliary_client.py — 1 line changed
  • tests/agent/test_auxiliary_explicit_base_url_anthropic.py — new test file

Changed files

  • agent/auxiliary_client.py (modified, +1/-1)
  • tests/agent/test_auxiliary_explicit_base_url_anthropic.py (added, +52/-0)

Code Example

model:
  default: kimi-for-coding
  provider: custom:kimi-coding-anthropic

custom_providers:
  - name: Kimi Coding Anthropic
    base_url: https://api.kimi.com/coding
    key_env: KIMI_API_KEY
    model: kimi-for-coding
    api_mode: anthropic_messages

---

⚠️  API call failed (attempt 1/3): NotFoundError [HTTP 404]
   🔌 Provider: kimi-coding  Model: kimi-for-coding
   🌐 Endpoint: https://api.kimi.com/coding/v1
   📝 Error: HTTP 404: The requested resource was not found

---

# Line ~2118
custom_base = _to_openai_base_url(explicit_base_url).strip()
# explicit_base_url = "https://api.kimi.com/coding"
# custom_base       = "https://api.kimi.com/coding/v1"  ← rewritten for OpenAI SDK

# Line ~2143-2145
client = OpenAI(api_key=custom_key, base_url=_clean_base, **extra)
client = _wrap_if_needed(client, final_model, custom_base, custom_key)
#                                                ^^^^^^^^^^^
#   Passes /coding/v1 to _maybe_wrap_anthropic → build_anthropic_client

---

base_url.raw_path + "/v1/messages".lstrip("/")
= /coding/v1/ + v1/messages
= /coding/v1/v1/messages  ← 404

---

# Before (buggy):
client = _wrap_if_needed(client, final_model, custom_base, custom_key)

# After (fixed):
# Pass the raw explicit_base_url, not the _to_openai_base_url()-rewritten version.
# AnthropicAuxiliaryClient needs the raw URL — the SDK appends /v1/messages.
client = _wrap_if_needed(client, final_model, explicit_base_url, custom_key)

---

# In _maybe_wrap_anthropic(), before build_anthropic_client():
anthropic_base = base_url.rstrip("/")
if anthropic_base.endswith("/v1"):
    anthropic_base = anthropic_base[:-3]  # strip trailing /v1
real_client = build_anthropic_client(api_key, anthropic_base)
RAW_BUFFERClick to expand / collapse

Auxiliary title generation 404 on custom Anthropic-mode providers (URL double-/v1)

Summary

When model.provider is a custom:<name> with api_mode: anthropic_messages and a base URL ending in /coding (e.g. Kimi Coding Plan), auxiliary tasks (title generation, compression, etc.) fail with HTTP 404 because the Anthropic SDK receives a base URL with /v1 already appended, then adds its own /v1/messages path — producing /coding/v1/v1/messages.

Reproduction

config.yaml:

model:
  default: kimi-for-coding
  provider: custom:kimi-coding-anthropic

custom_providers:
  - name: Kimi Coding Anthropic
    base_url: https://api.kimi.com/coding
    key_env: KIMI_API_KEY
    model: kimi-for-coding
    api_mode: anthropic_messages

Steps:

  1. Start a new conversation via Telegram or CLI
  2. First user message triggers title generation after the assistant responds
  3. Observe: ⚠ Auxiliary title generation failed: HTTP 404: The requested resource was not found

Docker logs:

⚠️  API call failed (attempt 1/3): NotFoundError [HTTP 404]
   🔌 Provider: kimi-coding  Model: kimi-for-coding
   🌐 Endpoint: https://api.kimi.com/coding/v1
   📝 Error: HTTP 404: The requested resource was not found

Root Cause

In resolve_provider_client() (auxiliary_client.py), the anonymous custom provider branch with explicit_base_url:

# Line ~2118
custom_base = _to_openai_base_url(explicit_base_url).strip()
# explicit_base_url = "https://api.kimi.com/coding"
# custom_base       = "https://api.kimi.com/coding/v1"  ← rewritten for OpenAI SDK

# Line ~2143-2145
client = OpenAI(api_key=custom_key, base_url=_clean_base, **extra)
client = _wrap_if_needed(client, final_model, custom_base, custom_key)
#                                                ^^^^^^^^^^^
#   Passes /coding/v1 to _maybe_wrap_anthropic → build_anthropic_client

_maybe_wrap_anthropic() detects the Kimi endpoint and wraps the client in AnthropicAuxiliaryClient via build_anthropic_client(api_key, base_url="https://api.kimi.com/coding/v1").

The Anthropic SDK constructs the request URL as:

base_url.raw_path + "/v1/messages".lstrip("/")
= /coding/v1/ + v1/messages
= /coding/v1/v1/messages  ← 404

Correct behavior: build_anthropic_client should receive https://api.kimi.com/coding (raw URL), producing /coding/v1/messages.

Why main chat works

The main agent loop uses a separate code path (runtime_provider.py) that correctly constructs the Anthropic client with the raw base URL. Only the auxiliary client's anonymous-custom + explicit_base_url branch has this bug.

The named custom provider branch (line ~2188-2207) and _try_custom_endpoint() (line ~1410-1424) already handle anthropic_messages correctly by calling build_anthropic_client directly with the raw URL.

Suggested Fix

One-line change in resolve_provider_client(), anonymous custom branch:

# Before (buggy):
client = _wrap_if_needed(client, final_model, custom_base, custom_key)

# After (fixed):
# Pass the raw explicit_base_url, not the _to_openai_base_url()-rewritten version.
# AnthropicAuxiliaryClient needs the raw URL — the SDK appends /v1/messages.
client = _wrap_if_needed(client, final_model, explicit_base_url, custom_key)

Defensive hardening (optional, covers all call sites): _maybe_wrap_anthropic() could strip a trailing /v1 from base_url before passing to build_anthropic_client, since the Anthropic SDK always appends its own /v1 prefix:

# In _maybe_wrap_anthropic(), before build_anthropic_client():
anthropic_base = base_url.rstrip("/")
if anthropic_base.endswith("/v1"):
    anthropic_base = anthropic_base[:-3]  # strip trailing /v1
real_client = build_anthropic_client(api_key, anthropic_base)

Impact

  • Affects: Any user with model.provider: custom:<name> where the custom provider uses api_mode: anthropic_messages (e.g. Kimi Coding Plan, third-party Anthropic-compatible gateways via anonymous custom config)
  • Does NOT affect: Named custom providers (configured via custom_providers: list), OpenRouter, Anthropic, OpenAI, or any provider that doesn't go through the anonymous custom + explicit_base_url path
  • Symptoms: Title generation fails silently (session gets NULL title); compression and other auxiliary tasks would also fail if they hit the same code path

Environment

  • Hermes Agent v2026.4.30-ssh (Docker)
  • anthropic SDK 0.96.0
  • Python 3.13

extent analysis

TL;DR

The most likely fix is to pass the raw explicit_base_url instead of the rewritten custom_base to _wrap_if_needed in resolve_provider_client().

Guidance

  • Identify the resolve_provider_client() function in auxiliary_client.py and locate the line where client is assigned using _wrap_if_needed.
  • Replace custom_base with explicit_base_url in the _wrap_if_needed call to ensure the raw base URL is used.
  • Consider adding defensive hardening in _maybe_wrap_anthropic() to strip any trailing /v1 from the base_url before passing it to build_anthropic_client.
  • Verify the fix by testing title generation and other auxiliary tasks with the updated code.

Example

# Before (buggy):
client = _wrap_if_needed(client, final_model, custom_base, custom_key)

# After (fixed):
client = _wrap_if_needed(client, final_model, explicit_base_url, custom_key)

Notes

This fix assumes that the explicit_base_url is correctly set to the raw base URL without any trailing /v1. If this is not the case, additional modifications may be necessary.

Recommendation

Apply the suggested fix by replacing custom_base with explicit_base_url in the _wrap_if_needed call, as this should resolve the issue with auxiliary title generation and other tasks.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

hermes - ✅(Solved) Fix Auxiliary title generation 404 on custom Anthropic-mode providers (URL double-/v1) [1 pull requests, 2 comments, 2 participants]