From 594eaa689a05e520981f1a966c75a7aaa44cdfac Mon Sep 17 00:00:00 2001 From: luozhancheng Date: Thu, 12 Feb 2026 09:15:26 +0800 Subject: [PATCH] fix(python): route Jupyter requests through client-proxy when E2B_SANDBOX_URL is set When E2B_SANDBOX_URL is set (local/self-hosted environments), the base SDK correctly routes envd API requests through the client-proxy via `get_sandbox_url()`. However, the code interpreter SDK's `_jupyter_url` property constructs its own URL using `get_host()`, bypassing E2B_SANDBOX_URL entirely. This causes Jupyter requests (run_code, etc.) to be sent to the production E2B API instead of the local client-proxy, resulting in 502 errors. This fix: - Updates `_jupyter_url` to use E2B_SANDBOX_URL when available - Adds `E2b-Sandbox-Id` and `E2b-Sandbox-Port` headers for local client-proxy routing (supported in local mode) - Applies to both sync and async Sandbox classes Cloud users are unaffected as E2B_SANDBOX_URL is not set in that flow. Co-authored-by: Cursor --- .../code_interpreter_async.py | 20 +++++++++++++++++++ .../code_interpreter_sync.py | 20 +++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/python/e2b_code_interpreter/code_interpreter_async.py b/python/e2b_code_interpreter/code_interpreter_async.py index de938240..da9c9210 100644 --- a/python/e2b_code_interpreter/code_interpreter_async.py +++ b/python/e2b_code_interpreter/code_interpreter_async.py @@ -58,8 +58,23 @@ class AsyncSandbox(BaseAsyncSandbox): @property def _jupyter_url(self) -> str: + # When E2B_SANDBOX_URL is set (local/self-hosted environment), route through the client-proxy + sandbox_url = self.connection_config._sandbox_url + if sandbox_url: + return sandbox_url return f"{'http' if self.connection_config.debug else 'https'}://{self.get_host(JUPYTER_PORT)}" + @property + def _jupyter_headers(self) -> Dict[str, str]: + """Extra headers for local client-proxy routing (E2b-Sandbox-Id / E2b-Sandbox-Port).""" + sandbox_url = self.connection_config._sandbox_url + if sandbox_url: + return { + "E2b-Sandbox-Id": self.sandbox_id, + "E2b-Sandbox-Port": str(JUPYTER_PORT), + } + return {} + @property def _client(self) -> AsyncClient: return AsyncClient(transport=self._transport) @@ -194,6 +209,7 @@ async def run_code( headers = { "Content-Type": "application/json", } + headers.update(self._jupyter_headers) if self._envd_access_token: headers["X-Access-Token"] = self._envd_access_token if self.traffic_access_token: @@ -260,6 +276,7 @@ async def create_code_context( headers = { "Content-Type": "application/json", } + headers.update(self._jupyter_headers) if self.traffic_access_token: headers["E2B-Traffic-Access-Token"] = self.traffic_access_token @@ -296,6 +313,7 @@ async def remove_code_context( headers = { "Content-Type": "application/json", } + headers.update(self._jupyter_headers) if self.traffic_access_token: headers["E2B-Traffic-Access-Token"] = self.traffic_access_token @@ -321,6 +339,7 @@ async def list_code_contexts(self) -> List[Context]: headers = { "Content-Type": "application/json", } + headers.update(self._jupyter_headers) if self.traffic_access_token: headers["E2B-Traffic-Access-Token"] = self.traffic_access_token @@ -355,6 +374,7 @@ async def restart_code_context( headers = { "Content-Type": "application/json", } + headers.update(self._jupyter_headers) if self.traffic_access_token: headers["E2B-Traffic-Access-Token"] = self.traffic_access_token diff --git a/python/e2b_code_interpreter/code_interpreter_sync.py b/python/e2b_code_interpreter/code_interpreter_sync.py index 3adb2804..23cb8d9f 100644 --- a/python/e2b_code_interpreter/code_interpreter_sync.py +++ b/python/e2b_code_interpreter/code_interpreter_sync.py @@ -55,8 +55,23 @@ class Sandbox(BaseSandbox): @property def _jupyter_url(self) -> str: + # When E2B_SANDBOX_URL is set (local environment), route through the client-proxy + sandbox_url = self.connection_config._sandbox_url + if sandbox_url: + return sandbox_url return f"{'http' if self.connection_config.debug else 'https'}://{self.get_host(JUPYTER_PORT)}" + @property + def _jupyter_headers(self) -> Dict[str, str]: + """Extra headers for local client-proxy routing (E2b-Sandbox-Id / E2b-Sandbox-Port).""" + sandbox_url = self.connection_config._sandbox_url + if sandbox_url: + return { + "E2b-Sandbox-Id": self.sandbox_id, + "E2b-Sandbox-Port": str(JUPYTER_PORT), + } + return {} + @property def _client(self) -> Client: return Client(transport=self._transport) @@ -190,6 +205,7 @@ def run_code( try: headers: Dict[str, str] = {"Content-Type": "application/json"} + headers.update(self._jupyter_headers) if self._envd_access_token: headers["X-Access-Token"] = self._envd_access_token if self.traffic_access_token: @@ -254,6 +270,7 @@ def create_code_context( try: headers: Dict[str, str] = {"Content-Type": "application/json"} + headers.update(self._jupyter_headers) if self._envd_access_token: headers["X-Access-Token"] = self._envd_access_token if self.traffic_access_token: @@ -290,6 +307,7 @@ def remove_code_context( try: headers: Dict[str, str] = {"Content-Type": "application/json"} + headers.update(self._jupyter_headers) if self._envd_access_token: headers["X-Access-Token"] = self._envd_access_token if self.traffic_access_token: @@ -315,6 +333,7 @@ def list_code_contexts(self) -> List[Context]: """ try: headers: Dict[str, str] = {"Content-Type": "application/json"} + headers.update(self._jupyter_headers) if self._envd_access_token: headers["X-Access-Token"] = self._envd_access_token if self.traffic_access_token: @@ -350,6 +369,7 @@ def restart_code_context( try: headers: Dict[str, str] = {"Content-Type": "application/json"} + headers.update(self._jupyter_headers) if self._envd_access_token: headers["X-Access-Token"] = self._envd_access_token if self.traffic_access_token: