{"record":{"id":"99ed450b0a378f18","repo":"JuliusBrussee/caveman","slug":"redirect-refused","errorCode":"redirect_refused","errorMessage":"redirect_refused","messagePattern":"redirect_refused","errorType":"error_code","errorClass":"MiddlewareError","httpStatus":null,"severity":"error","filePath":"packages/sdk/python/caveman_cloud/middleware/runtime.py","lineNumber":387,"sourceCode":"            connection.connect()\n            sock = connection.sock\n\n            def bound():\n                left = timeout - (time.monotonic() - started)\n                if self._closed:\n                    raise MiddlewareError(\"closed\")\n                if left <= 0:\n                    raise MiddlewareError(\"deadline\")\n                if sock is not None:\n                    sock.settimeout(left)\n\n            bound()\n            connection.request(\"POST\" if body is not None else \"GET\", PREFIX + path,\n                               body=body.encode(\"utf-8\") if body is not None else None, headers=headers)\n            bound()\n            with connection.getresponse() as response:\n                if 300 <= response.status < 400:\n                    raise MiddlewareError(\"redirect_refused\")\n                content = bytearray()\n                while True:\n                    bound()\n                    # read1 plus the remaining socket deadline bounds slow,\n                    # chunked bodies without buffering beyond the response cap.\n                    part = response.read1(min(65536, (4 << 20) + 1 - len(content)))\n                    if not part:\n                        break\n                    content.extend(part)\n                    if len(content) > 4 << 20:\n                        raise MiddlewareError(\"payload_limit\")\n                data = json.loads(content.decode(\"utf-8\"))\n                if not 200 <= response.status < 300:\n                    code = data.get(\"error\", {}).get(\"code\") if isinstance(data, dict) else None\n                    raise MiddlewareError(code if validate.token(code) else \"runtime_unavailable\")\n                return data\n        finally:\n            connection.close()","sourceCodeStart":369,"sourceCodeEnd":405,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/sdk/python/caveman_cloud/middleware/runtime.py#L369-L405","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nclient = MiddlewareClient(endpoint=\"http://mw.internal/api/\")  # server redirects to https\n// after\nclient = MiddlewareClient(endpoint=\"https://mw.internal/api\")  # direct, no redirect","handlingStrategy":"try-catch","validationCode":"import subprocess\ndef endpoint_redirects(url):\n    r = subprocess.run([\"curl\", \"-s\", \"-o\", \"/dev/null\", \"-w\", \"%{http_code}\", url], capture_output=True, text=True)\n    return r.stdout.strip().startswith(\"3\")\n# call before constructing the client; if True, fix the URL first","typeGuard":"def is_direct_endpoint(status: int) -> bool:\n    return 200 <= status < 300","tryCatchPattern":"try:\n    client.ready()\nexcept MiddlewareError as e:\n    if e.args[0] == \"redirect_refused\":\n        log.error(\"endpoint redirected; fix base URL scheme/path\")\n    raise","preventionTips":["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"],"tags":["network","http","redirect","python"],"backgroundTag":"invalid-url","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}