Summary
When using a custom OpenAI-compatible provider, Hermes can fail with HTTP 502: Upstream access forbidden even though the same API key, model, base URL, and JSON request body work with curl.
In my debugging, the failure was caused by the upstream OpenAI-compatible gateway rejecting requests that include the OpenAI Python SDK default headers, especially User-Agent: OpenAI/Python ... / X-Stainless-*. Sending the same request body with a plain curl-style user agent succeeds.
This is adjacent to, but distinct from, #21522. Disabling streaming helped rule out SSE as the cause, but the final blocker was request headers emitted by the SDK client.
Environment
- Hermes Agent:
v0.15.2 (2026.5.29.2)
- Python:
3.11.x
- OpenAI SDK:
2.24.0
- Provider config:
model.provider: custom
- Base URL shape:
http://<redacted-openai-compatible-host>/v1
- Model:
<redacted-custom-model-id>
- Platform/gateway: reproduced both through the messaging gateway and local
hermes -z
Symptoms
Hermes local one-shot and gateway requests failed with:
HTTP 502: Upstream access forbidden, please contact administrator
The messaging gateway itself was healthy:
- inbound messages were received
- the gateway was connected
- Hermes attempted model calls
- the final response sent back to the chat was the upstream error
So the failure looked like a chat/gateway problem at first, but it reproduced without the messaging platform via:
hermes -z 'Reply with only the number 1'
Reproduction Notes
With the same API key, same model, same base URL, and same endpoint:
Succeeds
curl http://<redacted-openai-compatible-host>/v1/chat/completions \
-H "Authorization: Bearer <redacted>" \
-H "Content-Type: application/json" \
-d '{
"model": "<redacted-custom-model-id>",
"messages": [{"role": "user", "content": "Reply with only the number 1"}],
"stream": false
}'
Fails
Using the OpenAI Python SDK:
from openai import OpenAI
client = OpenAI(
api_key="<redacted>",
base_url="http://<redacted-openai-compatible-host>/v1",
)
client.chat.completions.create(
model="<redacted-custom-model-id>",
messages=[{"role": "user", "content": "Reply with only the number 1"}],
)
The upstream returns:
HTTP 502: Upstream access forbidden
Also observed
Using httpx directly:
- minimal headers (
Authorization, Content-Type) succeeded
- adding
User-Agent: OpenAI/Python ... failed
- adding
X-Stainless-* headers also failed
Overriding the SDK client with a plain user agent worked:
client = OpenAI(
api_key="<redacted>",
base_url="http://<redacted-openai-compatible-host>/v1",
default_headers={"User-Agent": "curl/8.7.1"},
)
Workaround Used Locally
I patched the local Hermes install to set a plain user agent for this custom endpoint. After that:
hermes -z 'Reply with only the number 1'
returned:
I also added a local model.disable_streaming: true config flag while debugging. That helped avoid a separate class of custom-provider/SSE compatibility issues, but it was not sufficient by itself for this case.
Expected Behavior
Hermes should provide a supported way to configure request headers for custom OpenAI-compatible providers, or avoid hard-coding SDK-identifying headers when a provider is declared as generic custom.
Suggested Fix
Possible options:
- Add a documented config field such as:
model:
provider: custom
default: <model>
base_url: http://<host>/v1
default_headers:
User-Agent: curl/8.7.1
-
Add an environment variable override for custom-provider headers.
-
Add first-class support for model.disable_streaming: true or equivalent, so custom providers that do not handle SSE cleanly can opt out without patching site-packages.
-
If Hermes already has a profile/provider-header mechanism intended for this, document how to apply it to provider: custom.
Data Sanitization
All hostnames/IPs, API keys, app IDs, user IDs, chat IDs, message IDs, and personal filesystem paths have been removed or replaced with placeholders.
Summary
When using a
customOpenAI-compatible provider, Hermes can fail withHTTP 502: Upstream access forbiddeneven though the same API key, model, base URL, and JSON request body work withcurl.In my debugging, the failure was caused by the upstream OpenAI-compatible gateway rejecting requests that include the OpenAI Python SDK default headers, especially
User-Agent: OpenAI/Python .../X-Stainless-*. Sending the same request body with a plaincurl-style user agent succeeds.This is adjacent to, but distinct from, #21522. Disabling streaming helped rule out SSE as the cause, but the final blocker was request headers emitted by the SDK client.
Environment
v0.15.2 (2026.5.29.2)3.11.x2.24.0model.provider: customhttp://<redacted-openai-compatible-host>/v1<redacted-custom-model-id>hermes -zSymptoms
Hermes local one-shot and gateway requests failed with:
The messaging gateway itself was healthy:
So the failure looked like a chat/gateway problem at first, but it reproduced without the messaging platform via:
hermes -z 'Reply with only the number 1'Reproduction Notes
With the same API key, same model, same base URL, and same endpoint:
Succeeds
Fails
Using the OpenAI Python SDK:
The upstream returns:
Also observed
Using
httpxdirectly:Authorization,Content-Type) succeededUser-Agent: OpenAI/Python ...failedX-Stainless-*headers also failedOverriding the SDK client with a plain user agent worked:
Workaround Used Locally
I patched the local Hermes install to set a plain user agent for this custom endpoint. After that:
hermes -z 'Reply with only the number 1'returned:
I also added a local
model.disable_streaming: trueconfig flag while debugging. That helped avoid a separate class of custom-provider/SSE compatibility issues, but it was not sufficient by itself for this case.Expected Behavior
Hermes should provide a supported way to configure request headers for
customOpenAI-compatible providers, or avoid hard-coding SDK-identifying headers when a provider is declared as genericcustom.Suggested Fix
Possible options:
Add an environment variable override for custom-provider headers.
Add first-class support for
model.disable_streaming: trueor equivalent, so custom providers that do not handle SSE cleanly can opt out without patching site-packages.If Hermes already has a profile/provider-header mechanism intended for this, document how to apply it to
provider: custom.Data Sanitization
All hostnames/IPs, API keys, app IDs, user IDs, chat IDs, message IDs, and personal filesystem paths have been removed or replaced with placeholders.