JuliusBrussee/caveman · error · MiddlewareError
redirect_refused
redirect_refused
Error message
redirect_refused
What it means
MiddlewareError raised by the SDK's raw HTTP transport (_http) when the middleware server answers with a 3xx redirect status. The client deliberately refuses to follow redirects so that requests are never silently re-sent to a different host (which could leak tokens or body payloads). This means the configured endpoint URL points at something that redirects instead of serving the API directly.
Solutions
- Use the exact middleware API endpoint URL as configured server-side, matching scheme (https) and path so the server responds 200 directly
- Check server/proxy config and disable HTTP->HTTPS or host rewrites for the API path, or redirect at the client by changing the configured URL
- If a proxy is required, use one that forwards without redirecting, or handle the redirect target manually by updating the endpoint config
- Verify with curl -i <endpoint> that no 3xx is returned before the API response
Example fix
// before client = MiddlewareClient(endpoint="http://mw.internal/api/") # server redirects to https // after client = MiddlewareClient(endpoint="https://mw.internal/api") # direct, no redirect
Defensive patterns
Strategy: try-catch
Validate before calling
import subprocess
def endpoint_redirects(url):
r = subprocess.run(["curl", "-s", "-o", "/dev/null", "-w", "%{http_code}", url], capture_output=True, text=True)
return r.stdout.strip().startswith("3")
# call before constructing the client; if True, fix the URL first Type guard
def is_direct_endpoint(status: int) -> bool:
return 200 <= status < 300 Try / catch
try:
client.ready()
except MiddlewareError as e:
if e.args[0] == "redirect_refused":
log.error("endpoint redirected; fix base URL scheme/path")
raise Prevention
- Pin the exact https API endpoint URL in config; never use the web UI URL
- Disable HTTP->HTTPS redirects for the API path on proxies, or bake the final URL into config
- Add a startup health check (ready()) that fails fast with a clear message on 3xx
- Test endpoint config with curl -i before deploying
When it happens
Trigger: Any of ready(), optimize(), retrieve(), observe(), or delete_session() performing _http() when the middleware base URL returns 301/302/303/307/308. Typical cause: endpoint behind a load balancer or configured without/with a trailing path that triggers an HTTP->HTTPS or host-rewrite redirect.
Common situations: Configured base URL as http:// while the server forces https://; missing or extra trailing slash on the endpoint; proxy or gateway in front of the middleware issuing redirects; pointing the SDK at a web UI URL instead of the API endpoint.
Understand the failure class
Background: "Invalid URL" / "URL cannot be empty": fix the malformed or missing URL behind request-construction failures — this error's family across 50 libraries.
Related errors
- fetch failed: too many redirects
- fetch failed: unexpected redirect
- payload_limit
- runtime_unavailable
- awscreds: sts assume role with web identity failed
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/99ed450b0a378f18.
Report an issue: GitHub.
Appendix: source
Thrown at packages/sdk/python/caveman_cloud/middleware/runtime.py:387
connection.connect()
sock = connection.sock
def bound():
left = timeout - (time.monotonic() - started)
if self._closed:
raise MiddlewareError("closed")
if left <= 0:
raise MiddlewareError("deadline")
if sock is not None:
sock.settimeout(left)
bound()
connection.request("POST" if body is not None else "GET", PREFIX + path,
body=body.encode("utf-8") if body is not None else None, headers=headers)
bound()
with connection.getresponse() as response:
if 300 <= response.status < 400:
raise MiddlewareError("redirect_refused")
content = bytearray()
while True:
bound()
# read1 plus the remaining socket deadline bounds slow,
# chunked bodies without buffering beyond the response cap.
part = response.read1(min(65536, (4 << 20) + 1 - len(content)))
if not part:
break
content.extend(part)
if len(content) > 4 << 20:
raise MiddlewareError("payload_limit")
data = json.loads(content.decode("utf-8"))
if not 200 <= response.status < 300:
code = data.get("error", {}).get("code") if isinstance(data, dict) else None
raise MiddlewareError(code if validate.token(code) else "runtime_unavailable")
return data
finally:
connection.close()View on GitHub (pinned to 3ee70a1026)