#13932·OmniRoute

fix(api): rerank silently drops provider nodes unless the hostname is local

Author: cq-guojiaCreated Sep 17, 2026Updated Sep 17, 2026
Labelsbugacknowledgedapi

OmniRoute Version

Docker image diegosouzapw/omniroute:3.8.50-web. Also verified unchanged on release/v3.8.51 and on main — the filter quoted below is byte-for-byte identical on all three (including its comment).

Installation Method

Docker / Docker Compose

Operating System

Linux

OS Version

Debian-based container host (NAS)

Node.js Version

No response (Docker image default)

Provider(s) Involved

Custom OpenAI-compatible rerank provider node (prefix sf, baseUrl https://api.siliconflow.cn/v1). Same node serves /v1/embeddings correctly. Built-in registry provider siliconflow also involved (see "Impact" below).

Model(s) Involved

BAAI/bge-reranker-v2-m3

Client Tool

Any OpenAI-compatible client (reproduced with plain curl; originally hit via Hindsight / vectorize-io)

Description

POST /v1/rerank builds its local provider list from getCachedProviderNodes() but admits a node only if its baseUrl hostname passes this filter (src/app/api/v1/rerank/route.ts, v3.8.50 ≈ line 90; identical on release/v3.8.51 and main):

const hostname = new URL(n.baseUrl).hostname;
return hostname === "localhost" || hostname === "127.0.0.1"
  || /^172\.(1[6-9]|2[0-9]|3[0-1])\.\d{1,3}\.\d{1,3}$/.test(hostname);

Consequences:

  1. Any node on a public hostname is dropped — and so is any node on 10.x or 192.168.x, i.e. most real LAN setups.

  2. The drop is silent. .filter() has no else and no console.warn, and the enclosing try/catch swallows with a comment calling it non-critical. A dropped node produces zero log output.

  3. The error message points at the wrong thing. After the filter, localProviders.find(p => p.id === prefix) finds nothing, and the handler's final line returns:

    Invalid rerank model: <model>. Use format: provider/model
    

    which sends the operator looking at the model name, when the actual cause is the node's baseUrl hostname.

  4. The same node works on /v1/embeddings. That path has no hostname admission gate: the prefix fallback at src/lib/embeddings/service.ts:236-274 matches on n.prefix === provider with no host condition. Hence the exact symptom "works on embeddings, fails on rerank".

Why this looks unintentional rather than a deliberate restriction

  • #9096 already fixed a sibling gate on this same filter and was labelled fix(providers): audio-speech/transcriptions/translations provider nodes silently unroutable; its body names rerank/route.ts among the affected files and has a section titled "Why this looks unintentional rather than a deliberate restriction". That fix removed the apiType condition; the hostname condition was left behind.
  • #6925 fixed the embeddings analogue by demoting the private-host test from admission control to an auth-mode choice (authType: isNoAuthLocal ? "none" : "apikey"). /v1/rerank never received that change — the private-host test is still an admission gate here.
  • The inline comment claims "SSRF hardening", but real SSRF protection blocks outbound requests to internal networks, whereas this allows only internal networks — the opposite direction. The actual intent is visible in the buildDynamicRerankProvider comment ("Local OpenAI-compatible backends (oMLX, vLLM, etc.) expose /v1/rerank"): the regex is expressing the word local, not a security policy.

Impact: no path at all to a region-specific public rerank endpoint

For public SaaS rerank there are only two routes, and neither reaches a non-default region:

Route URL source Can it be changed?
Built-in registry (siliconflow, …) open-sse/config/rerankRegistry.tssiliconflow.baseUrl = "https://api.siliconflow.com/v1/rerank" No. Hardcoded constant; a connection's baseUrl is never read
provider_nodes the node's own baseUrl Only if the hostname passes the filter above (i.e. not a public host)

So e.g. the China-region endpoint https://api.siliconflow.cn/v1/rerank is unreachable through either route: the registry hardcodes .com, and a node pointing at .cn is dropped by the hostname filter. (Both domains accept the same key; .cn is ~7x faster from mainland China in my measurements — 0.17 s vs 1.26 s — so this is not academic.)

Note this is broader than my own case: PR #13732 documents the same failure for LAN and Tailscale peers running TEI / Infinity / vLLM / llama.cpp, because 10.x and 192.168.x are also excluded by the regex.

Steps to Reproduce

  1. Create a provider_nodes entry: prefix = "sf", baseUrl = "https://api.siliconflow.cn/v1", apiType includes rerank, with a valid API key stored on the node.
  2. Confirm the node works: POST /v1/embeddings {"model":"sf/BAAI/bge-m3","input":"ping"} → 200 (log line: EMBED Resolved custom embedding provider: sf -> https://api.siliconflow.cn/v1/embeddings).
  3. POST /v1/rerank with {"model":"sf/BAAI/bge-reranker-v2-m3","query":"q","documents":["a","b"],"top_n":2}.

Expected Behavior

The node is used and a results array is returned.

Actual Behavior

{"error":{"message":"Invalid rerank model: sf/BAAI/bge-reranker-v2-m3. Use format: provider/model",
"type":"invalid_request_error"}}

No log entry anywhere indicating the node was rejected for its hostname.

Diagnostic short-cut for other operators

The three terminal errors map to three distinct stages, which makes triage possible without log spelunking:

Error Stage
Invalid rerank model: … node was dropped by the hostname filter (this report)
No credentials for local provider: <prefix> node was admitted and prefix matched; the node simply has no key stored
No credentials for provider: <registry id> built-in registry route; unrelated to provider nodes

Related

  • PR #13732 (RERANK_REMOTE_PROVIDER_NODES) — addresses exactly this, but is still open, mergeable_state: dirty (conflicts with release/v3.8.51), 5 of 16 CI checks red (API Route Typecheck, Fast Quality Gates, 3 of 4 unit-test shards), with #13733 stacked on top of it; the flag does not appear anywhere in release/v3.8.51. I am filing this as an issue because #13732 is a PR with no tracking issue, and because its stated motivation is LAN/Tailscale self-hosted nodes — this report adds the public SaaS / region-specific endpoint use case, which is a different affected population with the same root cause.
  • #9096 — fixed the apiType gate on the same filter; hostname gate left behind
  • #6925 — fixed the embeddings analogue (auth-mode instead of admission)
  • #5332 — registry coverage for Qwen3-Reranker (different: registry, not nodes)

Additional notes

  • The rerank node path hardcodes authType: "apikey" / authHeader: "bearer" and always sends Authorization: Bearer <token>. Unlike embeddings there is no no-auth branch for private hosts.
  • /v1/rerank does not consult combos at all (the route never queries them), so rerank failover cannot be expressed as a combo — worth documenting separately, since operators who set up embedding combos naturally assume the same works for rerank.