Skip to content

axios requests fail with ERR_BAD_RESPONSE inside NemoClaw sandbox — double proxy conflict with NODE_USE_ENV_PROXY #2109

@lcsmontiel

Description

@lcsmontiel

Description

Outbound HTTPS requests made via axios fail inside a NemoClaw sandbox with ERR_BAD_RESPONSE: stream has been aborted. The OpenShell L7 proxy logs HTTP:UNKNOWN [INFO] DENIED UNKNOWN.

What happened: Any axios.get() or axios.post() to an external HTTPS endpoint fails. The L7 proxy closes the connection. This affects any OpenClaw plugin, skill, or tool that uses axios for outbound HTTP calls.

What I expected: axios HTTPS requests should work the same as https.request() from the same sandbox — which succeeds.

Root cause: axios has its own proxy handling that reads HTTP_PROXY/HTTPS_PROXY from the environment. Simultaneously, NODE_USE_ENV_PROXY=1 (baked into the NemoClaw container image) intercepts all http.request() / https.request() calls at the Node.js 22 engine level. When both are active, the request gets double-processed — axios configures it for HTTP forward proxy, then Node.js re-processes it through its own proxy logic. The result is a malformed request with the proxy port leaked into the destination URL:

https://clawhub.ai:3128/   ← :3128 is the proxy port, should be :443

The L7 proxy cannot parse this and rejects it as HTTP:UNKNOWN DENIED UNKNOWN.

Key finding: Node.js core https.request() works correctly through the proxy. The bug is specifically the interaction between axios and NODE_USE_ENV_PROXY.

What works vs what fails:

HTTP client | Status | Why -- | -- | -- https.request() (Node.js core) | PASS | NODE_USE_ENV_PROXY handles CONNECT tunnel correctly alone curl | PASS | Own CONNECT tunnel implementation jwks-rsa | PASS | Uses https.request() internally @azure/msal-node | PASS | Has its own HTTP client axios (default) | FAIL | Double proxy: axios + NODE_USE_ENV_PROXY conflict axios (proxy:false) | PASS | Axios skips proxy, NODE_USE_ENV_PROXY handles correctly fetch() (undici) | FAIL | Similar double proxy conflict

Proof that disabling axios proxy fixes it:

# FAILS — axios default (double proxy conflict)
node -e "require('/usr/local/lib/node_modules/openclaw/node_modules/axios').get(
  'https://clawhub.ai',
  {timeout:10000, headers:{'User-Agent':'test'}}
).then(r => console.log('OK', r.status)).catch(e => console.log(e.code, e.message))"
# → ERR_BAD_RESPONSE stream has been aborted

# WORKS — axios with proxy:false (NODE_USE_ENV_PROXY handles it alone)
node -e "require('/usr/local/lib/node_modules/openclaw/node_modules/axios').get(
'https://clawhub.ai',
{timeout:10000, proxy:false, headers:{'User-Agent':'test'}}
).then(r => console.log('OK', r.status)).catch(e => console.log(e.code, e.message))"

# → OK 200

Suggested fix: Since NODE_USE_ENV_PROXY=1 correctly handles HTTPS proxy via CONNECT tunnel, axios's own proxy handling is redundant and conflicting. Either:

  • Set axios.defaults.proxy = false in the openclaw build (the bundled dist, not node_modules)
  • Or remove NODE_USE_ENV_PROXY=1 from the Dockerfile and use a proxy preload that doesn't conflict with axios

Workaround: We run a proxy translator on 127.0.0.1:3129 that intercepts the malformed requests and converts them to proper CONNECT tunnels.

Reproduction Steps

Deploy NemoClaw and run nemoclaw onboard

Enter the sandbox:

openshell sandbox connect my-assistant
Confirm environment:

echo $HTTPS_PROXY $NODE_USE_ENV_PROXY

http://10.200.0.1:3128 1

Run the diagnostic:

node -e "
const https = require('https');
const axios = require('/usr/local/lib/node_modules/openclaw/node_modules/axios');
const TARGET = 'https://clawhub.ai';
const UA = {headers:{'User-Agent':'nemoclaw-repro'}};

// Test 1: Node.js core https.request — should PASS
https.get(TARGET, UA, r => {
console.log('Test 1 https.request():', r.statusCode >= 200 && r.statusCode < 400 ? 'PASS' : 'FAIL', 'HTTP', r.statusCode);
r.resume();

// Test 2: axios default — should FAIL
axios.get(TARGET, {timeout:10000, ...UA})
.then(r => console.log('Test 2 axios (default):', 'PASS', 'HTTP', r.status))
.catch(e => {
console.log('Test 2 axios (default):', 'FAIL', e.code, e.message);

  // Test 3: axios proxy:false — should PASS
  axios.get(TARGET, {timeout:10000, proxy:false, ...UA})
    .then(r => console.log('Test 3 axios (proxy:false):', 'PASS', 'HTTP', r.status))
    .catch(e => console.log('Test 3 axios (proxy:false):', e.response ? 'PASS HTTP '+e.response.status : 'FAIL '+e.code));
});

}).on('error', e => console.log('Test 1 https.request():', 'FAIL', e.code));
"
Expected output (bug present):

Test 1 https.request(): PASS HTTP 200
Test 2 axios (default): FAIL ERR_BAD_RESPONSE stream has been aborted
Test 3 axios (proxy:false): PASS HTTP 200

Environment

  • OS: Ubuntu 22.04 (EC2 t3.large, ca-central-1)
  • Node.js: v22.22.1 (ships with NemoClaw sandbox image)
  • Docker: Docker Engine (via NemoClaw bootstrap)
  • NemoClaw: latest (git clone, commit d74a122 / 2026.4.2)
  • OpenShell: v0.0.26 (pinned — NemoClaw rejects 0.0.27+)
  • OpenClaw: 2026.4.2
  • axios: 1.12.0 (from @microsoft/teams.apps dependency, also bundled in openclaw dist/)

nemoclaw-debug.tar.gz

Debug Output

Logs

ubuntu@ip-10-194-6-227:~$ nemoclaw my-assistant

  ✓ Connecting to sandbox 'my-assistant'
  Inside the sandbox, run `openclaw tui` to start chatting with the agent.
  Type `exit` (or Ctrl-D) to return to the host shell.

sandbox@my-assistant:~$ node -e "
const https = require('https');
const axios = require('/usr/local/lib/node_modules/openclaw/node_modules/axios');
const TARGET = 'https://clawhub.ai';
const UA = {headers:{'User-Agent':'nemoclaw-repro'}};

// Test 1: Node.js core https.request — should PASS
https.get(TARGET, UA, r => {
  console.log('Test 1 https.request():', r.statusCode >= 200 && r.statusCode < 400 ? 'PASS' : 'FAIL', 'HTTP', r.statusCode);
  r.resume();

  // Test 2: axios default — should FAIL
  axios.get(TARGET, {timeout:10000, ...UA})
    .then(r => console.log('Test 2 axios (default):', 'PASS', 'HTTP', r.status))
    .catch(e => {
      console.log('Test 2 axios (default):', 'FAIL', e.code, e.message);

      // Test 3: axios proxy:false — should PASS
      axios.get(TARGET, {timeout:10000, proxy:false, ...UA})
        .then(r => console.log('Test 3 axios (proxy:false):', 'PASS', 'HTTP', r.status))
        .catch(e => console.log('Test 3 axios (proxy:false):', e.response ? 'PASS HTTP '+e.response.status : 'FAIL '+e.code));
    });
}).on('error', e => console.log('Test 1 https.request():', 'FAIL', e.code));
"
(node:4396) [UNDICI-EHPA] Warning: EnvHttpProxyAgent is experimental, expect them to change at any time.
(Use `node --trace-warnings ...` to show where the warning was created)
Test 1 https.request(): PASS HTTP 200
Test 2 axios (default): FAIL ERR_BAD_RESPONSE stream has been aborted
Test 3 axios (proxy:false): PASS HTTP 200
sandbox@my-assistant:~$ exit
exit
ubuntu@ip-10-194-6-227:~$ openshell logs | tail -5
→ Using sandbox 'my-assistant' (last used)
[1776710692.914] [sandbox] [OCSF ] [ocsf] NET:OTHER [MED]
[1776710692.914] [sandbox] [OCSF ] [ocsf] NET:OPEN [INFO] ALLOWED /usr/local/bin/node(4396) -> clawhub.ai:443 [policy:clawhub engine:opa]
[1776710692.926] [sandbox] [OCSF ] [ocsf] HTTP:GET [INFO] ALLOWED GET http://clawhub.ai/ [policy:clawhub]
[1776710693.061] [sandbox] [OCSF ] [ocsf] HTTP:UNKNOWN [INFO] DENIED UNKNOWN 
[1776710693.066] [sandbox] [OCSF ] [ocsf] HTTP:GET [INFO] ALLOWED GET http://clawhub.ai/ [policy:clawhub]

Checklist

  • I confirmed this bug is reproducible
  • I searched existing issues and this is not a duplicate

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: cliCommand line interface, flags, terminal UX, or outputarea: sandboxOpenShell sandbox lifecycle, runtime, config, or recovery

    Type

    No fields configured for Bug.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions