Skip to content

fix(config): bypass proxy for loopback backend calls - #924

Open
ajdevy wants to merge 1 commit into
iOfficeAI:mainfrom
ajdevy:fix/config-cli-bypass-proxy-for-loopback
Open

fix(config): bypass proxy for loopback backend calls#924
ajdevy wants to merge 1 commit into
iOfficeAI:mainfrom
ajdevy:fix/config-cli-bypass-proxy-for-loopback

Conversation

@ajdevy

@ajdevy ajdevy commented Aug 22, 2026

Copy link
Copy Markdown

Problem

aioncore config builds its HTTP client with reqwest::Client::new(), which honours HTTP_PROXY/HTTPS_PROXY and has no loopback exemption. The backend listens on loopback, so any proxy exported for unrelated traffic captures these local calls and no config subcommand works.

The failure mode actively misleads. The CLI reports:

CONFIG_HTTP_STATUS_ERROR command="config skills list" path="/api/skills" status="503": AionUi backend returned an error status

That accuses the backend, but the backend is healthy — it serves 200 for the same route at the same moment, and the failing requests are absent from aioncore.log entirely, because they never reach the router. config capabilities keeps working, since it answers locally without touching the backend, which makes the CLI look partly alive and reinforces the wrong diagnosis.

I lost a while to this: I checked backend uptime, the DB lock files and the WAL, and concluded the backend was rejecting my session. The actual cause was one environment variable. Any tool that exports a proxy for its own traffic breaks every subsequent config call in that shell — in my case claude-vps sets HTTP_PROXY=http://127.0.0.1:13128 for its tunnel.

Reported as #4145 on iOfficeAI/AionUi.

Fix

Build the client with no_proxy() when AIONUI_BASE_URL points at loopback. A remote base URL keeps the proxy environment, since reaching it may legitimately depend on the proxy. An unparseable or absent base URL is handled conservatively.

Verification

Same machine, same running backend, proxy exported in both cases:

Binary config skills list
Shipped 2.1.59 CONFIG_HTTP_STATUS_ERROR ... status="503"
This branch success: true, 50 skills

Gates, all green on aionui-app:

  • cargo test -p aionui-app --bin aioncore cmd_config::tests — 5 passed, 0 failed (2 new)
  • cargo fmt --all -- --check — clean
  • cargo clippy -p aionui-app --bin aioncore -- -D warnings — clean

The two added tests cover loopback IPv4 including the wider 127.0.0.0/8, localhost case-insensitively, the [::1] bracketed IPv6 literal, and the negative cases — a LAN address, a public host, localhost.example.com (a host that merely starts with the loopback label), and an unparseable base URL.

Note on a separate issue

While working around this I hit a second problem, not addressed here since it is independent: config skills import reports per-skill rejections inside data.failed[] while still setting success: true on the envelope.

{"success": true, "data": {"skill_name": "", "failed": [{"source_name": "kazoo-github-pr", "code": "SKILL_INVALID_FRONTMATTER"}]}}

Importing a parent directory made this concrete: 23 of 25 skills imported, two were dropped, and the envelope still said success: true. A caller that checks success — which the agent-facing contract encourages — cannot distinguish a full import from a partial one. Happy to send a second PR if you want the envelope to reflect partial failure.

The config CLI built its HTTP client with reqwest::Client::new(), which
honours HTTP_PROXY/HTTPS_PROXY and has no loopback exemption. The AionUi
backend listens on loopback, so any proxy exported for unrelated traffic
captured these local calls.

The failure was misleading: requests never reached the router, so they
were absent from aioncore.log while the backend served 200 for the same
route, and the CLI reported CONFIG_HTTP_STATUS_ERROR ... status="503":
AionUi backend returned an error status. That points at a healthy
backend. config capabilities kept working because it answers locally,
which made the CLI look partly alive and reinforced the wrong diagnosis.

Build the client with no_proxy() when AIONUI_BASE_URL is loopback. A
remote base URL keeps the proxy environment, since reaching it may
depend on the proxy.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant