{"record":{"id":"a0e8be7c70a5b340","repo":"JuliusBrussee/caveman","slug":"expected-an-httpx2-basetransport","errorCode":null,"errorMessage":"Expected an HTTPX2 BaseTransport","messagePattern":"Expected an HTTPX2 BaseTransport","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/middleware/python/caveman_middleware/_httpx2.py","lineNumber":41,"sourceCode":"            original.logical_call_id, str(uuid.uuid4()), optimization=original.optimization, plan_id=original.plan_id)\n        self.attempt_count += 1\n        try:\n            attempt.wire_sha256 = hashlib.sha256(request.content).hexdigest()\n        except httpx2.RequestNotRead:\n            pass\n        return attempt\n\n\nclass CavemanOpenAITransport(httpx2.BaseTransport):\n    \"\"\"Wrap an application-owned HTTPX2 transport before client construction.\n\n    Pass this same object as ``transport`` to ``with_caveman_openai``. Requests\n    outside that native wrapper pass through. The supplied transport retains its\n    connection pool, TLS/proxy settings, and close behavior; this adds no retries.\n    \"\"\"\n    def __init__(self, transport):\n        if not isinstance(transport, httpx2.BaseTransport):\n            raise TypeError(\"Expected an HTTPX2 BaseTransport\")\n        self.transport = transport\n\n    def handle_request(self, request):\n        call = owner.get()\n        if not isinstance(call, OpenAICall) or call.transport is not self:\n            return self.transport.handle_request(request)\n        attempt = call.next_attempt(request)\n        attempt.observe(\"dispatch_intent\")\n        token = owner.set(attempt)\n        try:\n            response = self.transport.handle_request(request)\n            observe_response(response, attempt)\n            return response\n        except BaseException:\n            attempt.observe(\"failed\")\n            raise\n        finally:\n            owner.reset(token)","sourceCodeStart":23,"sourceCodeEnd":59,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/middleware/python/caveman_middleware/_httpx2.py#L23-L59","documentation":"CavemanOpenAITransport wraps an existing httpx2 transport so OpenAI calls can be intercepted while everything else passes through untouched. It validates at construction time that the supplied object is actually an httpx2.BaseTransport, since it delegates handle_request to it.","triggerScenarios":"Constructing CavemanOpenAITransport(None), with an httpx (v1) transport instead of httpx2, with a raw HTTPAdapter/requests object, or with a client instead of a transport.","commonSituations":"Mixing httpx and httpx2 imports after a migration; passing httpx.AsyncHTTPTransport into the sync wrapper; copying examples from the httpx (non-2) docs; passing the transport= argument of httpx2.Client wrongly wrapped.","solutions":["Pass an instance of httpx2.BaseTransport (e.g. httpx2.HTTPTransport()) as transport","If migrating from httpx, replace httpx.* imports with httpx2.* for the transport object","Use CavemanAsyncOpenAITransport with an httpx2.AsyncBaseTransport for async clients","Check the object with isinstance before constructing"],"exampleFix":"# before\ntransport = CavemanOpenAITransport(httpx.HTTPTransport())  # TypeError\n# after\ntransport = CavemanOpenAITransport(httpx2.HTTPTransport())\nclient = with_caveman_openai(httpx2.Client(transport=transport))","handlingStrategy":"type-guard","validationCode":"def ensure_sync_transport(transport):\n    import caveman_middleware._httpx2 as m\n    if not isinstance(transport, m.httpx2.BaseTransport):\n        raise TypeError(\"transport must be an httpx2.BaseTransport\")\n    return transport","typeGuard":"def is_sync_transport(transport) -> bool:\n    return isinstance(transport, httpx2.BaseTransport)","tryCatchPattern":"try:\n    transport = CavemanOpenAITransport(candidate)\nexcept TypeError as e:\n    if \"BaseTransport\" in str(e):\n        candidate = httpx2.HTTPTransport()\n        transport = CavemanOpenAITransport(candidate)","preventionTips":["Import transports from httpx2, never httpx, in middleware wiring","Use AsyncHTTPTransport with CavemanAsyncOpenAITransport and HTTPTransport with the sync one","Assert transport type in a startup self-test","After httpx->httpx2 migrations, grep for leftover httpx transport constructors"],"tags":["python","httpx2","transport","type-error"],"backgroundTag":"type-mismatch","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"}