-
Notifications
You must be signed in to change notification settings - Fork 8
Admin Routstr provider balance check returns 500 on upstream ConnectTimeout #399
Description
Summary
GET /admin/api/upstream-providers/{provider_id}/balance can raise an unhandled httpx.ConnectTimeout when the upstream provider is another Routstr node and that node is slow or unreachable.
Instead of returning a controlled upstream error, the request falls through to the general exception handler and becomes a 500.
Affected versions
- Reproduced in
v0.3.0 - Still present on branch
v0.4.0as of 2026-03-13
Reproduction
- Configure an upstream provider with
provider_type == "routstr" - Point
base_urlat a Routstr node that is slow, unreachable, or timing out on TCP connect - Call
GET /admin/api/upstream-providers/{provider_id}/balance - Observe
httpx.ConnectTimeoutand a 500 response
Current behavior
The admin route special-cases Routstr providers and calls the upstream node directly:
routstr/core/admin.pylines 1025-1046 on currentv0.4.0- raw
httpx.AsyncClient().get(f"{clean_url}/v1/balance/info", ...) - no
try/exceptforhttpx.TimeoutException/httpx.RequestError
This produces logs like:
error_type: ConnectTimeout- path:
/admin/api/upstream-providers/4/balance
Expected behavior
The admin balance endpoint should not crash on upstream timeout.
It should either:
- return a controlled
502/504with a useful error message, or - return
{ "ok": false, "balance_data": null }consistently for unreachable upstreams
but it should not become an unhandled 500.
Additional context
There is already a RoutstrUpstreamProvider.get_balance() implementation in routstr/upstream/routstr.py that wraps the request and returns None on failure. The admin route currently bypasses that abstraction and duplicates the HTTP call.
There was a related v0.4.0 change in commit 4643aef ("no auth for routstr node to check and topup balance"), but that only makes the auth header optional. It does not address timeout handling.
There are also related timeout issues/fixes elsewhere in the repo:
- 504 Gateway Timeout error when using api.nonkycai.com node #283 general upstream timeout report
- Routstr node deducting max cost when returning 443 timeout bug #314 / make sure to refund #315 fixed payment reversion on timeout for proxied upstream requests
Those do not fix this admin balance path.
Possible fix direction
- Remove the Routstr-specific raw HTTP branch from
get_provider_balance - Instantiate the provider via
_instantiate_provider(provider)for all provider types - Let
RoutstrUpstreamProvider.get_balance()own the Routstr balance fetch behavior - Normalize failures into a controlled API response or explicit
HTTPException - Add regression tests for timeout and non-200 upstream responses
Secondary observation
The error log for this path may show request_id: no-request-id even though general_exception_handler() passes request_id in extra. RequestIdFilter appears to overwrite record.request_id from the context var fallback in routstr/core/logging.py. That makes debugging harder, but it is separate from the 500 timeout bug.