Files
devin-ai-integration[bot] b8029973aa fix(sdk): guard egressProxy shape in JS and drop null proxy credentials (#1757)
## Summary

Follow-up to #1688. Three fixes on the `network.egressProxy` /
`network["egress_proxy"]` paths a caller reaches by bypassing the types,
plus the one changelog snippet that does not run.

**The guards disagreed.** Python raised `InvalidArgumentException` for a
proxy without a string `address`; JS had none, and since
`buildEgressProxyBody` rebuilds the body from the known fields, an
address that isn't there simply vanished and the caller got an API error
about a `{}` they never wrote. Nothing leaked — `address` is `required`
in the spec, so all of these already failed closed at the API — but the
error named the wrong thing.

```ts
function buildEgressProxyBody(egressProxy: SandboxEgressProxyOpts) {
+  if (!isPlainObject(egressProxy) || typeof egressProxy.address !== 'string') {
+    throw new InvalidArgumentError(
+      `network egressProxy must be an object with a string 'address' (e.g. 'proxy.example.com:1080').`
+    )
+  }
```

| caller passes | JS before | JS now | Python |
|---|---|---|---|
| `{}` | `{"egressProxy":{}}` | `InvalidArgumentError` |
`InvalidArgumentException` |
| `{address: 1080}` | `{"egressProxy":{"address":1080}}` |
`InvalidArgumentError` | `InvalidArgumentException` |
| `'proxy.example.com:1080'` | `{"egressProxy":{}}` — address dropped |
`InvalidArgumentError` | `InvalidArgumentException` |

**`null` credentials reached the wire.** Both SDKs checked `!==
undefined` / `in`, so a `null` / `None` username or password serialized
as a JSON null the API rejects (`type: string`). Reading a credential
out of an unset environment variable is the way to land there, and it is
the one case where the caller means "this proxy takes no credentials" —
both now skip a nullish credential (`!= null` in JS, `.get(...) is not
None` in Python).

```ts
await Sandbox.create({
  network: {
    egressProxy: {
      address: 'proxy.example.com:1080',
      // Unset in the environment; the proxy takes no credentials.
      username: process.env.PROXY_USER,
    },
  },
})
```

```python
Sandbox.create(
    network={
        "egress_proxy": {
            "address": "proxy.example.com:1080",
            "username": os.environ.get("PROXY_USER"),
        },
    },
)
```

**The published `get_info` snippet.** The 2.41.0 entry in both
changelogs printed `info.network["egress_proxy"]`, but
`SandboxInfo.network` is `Optional` and `egress_proxy` is `NotRequired`
— pyright rejects it (`reportOptionalSubscript`,
`reportTypedDictNotRequiredAccess`) and it raises at runtime for a
sandbox without a proxy, unlike the TS line right above it
(`info.network?.egressProxy`). It now reads `print((info.network or
{}).get("egress_proxy"))`.

Tests cover the three rejected JS shapes and the null credentials in
both SDKs.


Link to Devin session:
https://app.devin.ai/sessions/216bb02ad7924e4ba5f2968aaadc7938
Requested by: @mishushakov

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: mish@e2b.dev <mish@e2b.dev>
2026-08-21 20:00:22 +00:00
..