Skip to content

Extension install fails with Server returned 403 when an HTTP proxy denies the gallery host, even though the host is listed in NO_PROXY / http.noProxy (Positron Pro on Workbench) #15177

Description

@emcrisan

System details:

Positron and OS details:

Positron Pro 2025.12.2 (build 5)
Code - OSS Version: 1.106.0
Posit Workbench 2026.01.0+392.pro5
OS: RHEL 9.6 (x86_64), standalone VM, Local Job Launcher
Session type: browser-based Positron Pro session (positron-server)

The behavior above was reported by the administrator of the affected deployment. We have not
reproduced it on a Posit-side test system; the files involved appear unchanged between the tag
matching this build and current main, but we have not verified a newer build empirically.

Session details:

Not interpreter-specific; occurs before any interpreter interaction, on a fresh session with no
extensions installed.

Describe the issue:

In a Positron Pro session on Workbench, with HTTP_PROXY/HTTPS_PROXY present in the environment
and a corporate proxy that returns 403 for the extension gallery host:

  • Browsing and searching the extension gallery works.

  • Installing any extension fails immediately with:

    Server returned 403
        at ExtensionGalleryServiceWithNoStorageService2.getManifest
        (installGalleryExtensions → checkAndGetCompatibleVersion → getManifest)
    

The gallery host is listed in NO_PROXY/no_proxy at the OS level, and curl from inside the same
session honors that and reaches the gallery host successfully. Adding http.noProxy for the same
host — both as an administrator-seeded default for all sessions and in individual user settings —
does not change the behavior. The failure is deterministic: it recurs after a Workbench restart and
in a brand-new session.

Steps to reproduce the issue:

Two parts: what the administrator actually did in the affected deployment, and a self-contained
recipe we believe should reproduce it on a clean host. It is
written from the reported symptoms plus source reading, and the outcomes in it are predictions, not
observations.

As reported in the affected deployment

  1. Configure a self-hosted VS Code extensions gallery and point sessions at it with
    EXTENSIONS_GALLERY in /etc/rstudio/launcher-env.
  2. Have all outbound traffic forced through a corporate proxy that returns 403 for that gallery
    host, with the proxy variables set system-wide and in the rstudio-server service environment,
    and the gallery host present in NO_PROXY/no_proxy at every layer.
  3. Restart Workbench, open a fresh Positron Pro session, search the Extensions pane (returns
    results), then install an extension.

Reported result: install fails immediately with the Server returned 403 / getManifest error.
Reported controls from the same host: curl --noproxy '*' to the gallery and asset URLs returns
200; the same curl forced through the proxy returns 403; a plain Node https.get() returns 200.
Adding http.noProxy for the gallery host changed nothing.

Self-contained recipe (untested by us)

Needs one Linux host with Workbench + Positron Pro and outbound internet. Uses open-vsx.org as the
gallery so no private infrastructure is involved.

  1. Install a forward proxy that denies the gallery host and allows everything else:

    sudo dnf install -y squid
    sudo tee /etc/squid/squid.conf >/dev/null <<'EOF'
    http_port 3128
    acl gallery dstdomain open-vsx.org
    http_access deny gallery
    http_access allow all
    EOF
    sudo systemctl enable --now squid
  2. Confirm the two control paths, which should show 200 direct and 403 through the proxy:

    curl -s -o /dev/null -w 'direct:  %{http_code}\n' --noproxy '*' https://open-vsx.org/
    curl -s -o /dev/null -w 'proxied: %{http_code}\n' -x http://localhost:3128 https://open-vsx.org/
  3. Attempt an install with the gallery host present in no_proxy:

    env HTTP_PROXY=http://localhost:3128 HTTPS_PROXY=http://localhost:3128 \
        NO_PROXY=open-vsx.org no_proxy=open-vsx.org \
      /usr/lib/rstudio-server/bin/positron-server/bundled/bin/positron-server \
        --install-extension redhat.vscode-yaml \
        --extensions-dir /tmp/repro-exts --user-data-dir /tmp/repro-udd --log trace

    Expected if this matches the reported behavior: the install fails carrying the proxy's 403,
    despite no_proxy covering the host.

  4. Re-run the identical command with only HTTP_PROXY/HTTPS_PROXY removed, keeping no_proxy.
    Expected: the install succeeds.

We chose the CLI form because --install-extension appears to construct the same
RequestService('remote') and ExtensionGalleryServiceWithNoStorageService as the in-session
install (src/vs/server/node/remoteExtensionHostAgentCli.ts), which would make it a smaller
reproducer than driving the UI — but that equivalence is also inferred from source rather than
tested, so the UI path in the reported-deployment section above is the authoritative symptom.

Expected or desired behavior:

Hosts listed in NO_PROXY/no_proxy (and/or in http.noProxy) are not routed through the proxy by
the extension gallery request path — or, if that is not the intent, some supported way to exempt the
gallery host, and documentation stating that the bypass variables do not apply here. Today an
administrator whose proxy denies the gallery host appears to have no in-product configuration that
makes gallery installs work.

Were there any error messages in the UI, Output panel, or Developer Tools console?

The UI error is the Server returned 403 / getManifest message quoted above.

A Server output-channel trace (Developer: Set Log Level…ServerTrace) has been
requested from the reporting administrator and is not yet captured.

Observations that may be relevant

Offered as triage context, not as a conclusion. Items 1-4 are reported by the administrator of the
affected deployment; item 5 is our own reading of the source; items 6-7 are inferences from that
reading and are not confirmed by observation.

  1. Search works while install fails — search results render normally in the same session in which
    the install fails, suggesting the query and the install may not take the same network path.
  2. The same URLs are reachable from the same hostcurl --noproxy '*' and a plain Node
    https.get() both returned 200 for the gallery and asset URLs; only the in-product install fails.
    Forcing the same curl through the proxy returned 403, so the 403 text is consistent with what
    the proxy returns for that host.
  3. The bypass variables are present and effective for other clientsenv | grep -i proxy
    inside the session shows the gallery host in no_proxy, and curl in that same session honors
    it.
  4. http.noProxy made no difference at either settings layer tried (administrator-seeded
    defaults for all sessions, and individual user settings).
  5. Source reading we did while triaging, which may or may not be pertinent:
    • src/vs/platform/request/node/proxy.tsgetSystemProxyURI() reads HTTP_PROXY/HTTPS_PROXY
      (and lowercase) and getProxyAgent() returns an agent whenever a proxy URL is found; we did not
      find a bypass-list check on this path.
    • src/vs/platform/request/node/requestService.tsconfigure() reads http.proxy,
      http.proxyStrictSSL, and http.proxyAuthorization; we did not find a read of http.noProxy
      here.
    • git grep -i no_proxy -- src/ returns a single hit, in
      src/vs/platform/windows/electron-main/windowImpl.ts (Electron main process). http.noProxy
      additionally appears in src/vs/workbench/api/node/proxyResolver.ts, which covers extension-host
      requests.
    • src/vs/server/node/serverServices.ts registers ExtensionGalleryServiceWithNoStorageService
      and a RequestService('remote', …) in the server container.
    • These files look the same at main as at the tag matching the affected build, so we do not
      expect a newer Positron to behave differently — though we would be glad to be wrong about that.
  6. The failure appears to involve two requests, not one. Consistent with getAsset retrying
    against the fallback asset URI when the primary request fails, and with the gallery in use
    publishing an identical fallback URI. Worth knowing when reading a trace log so the pair is not
    misread as two separate attempts.
  7. RequestService.request() resolves the login shell environment and merges it over
    process.env.
    The administrator's attempt to clear the proxy variables only in Workbench's
    session-launch configuration did not change the behavior; we have not confirmed which layer
    re-supplied them, and this merge is one candidate. Mentioning it because it may affect how you
    reproduce.

Questions for triage

  1. Is the extension gallery request path intended to honor NO_PROXY/no_proxy or http.noProxy?
    If not, is that documented anywhere we should be pointing administrators at?
  2. Is there a supported way today for an administrator to exempt the gallery host from the proxy for
    Positron Pro sessions, short of removing the proxy variables from the whole machine?
  3. Are VS Code (code-server) sessions on Workbench expected to behave the same way? Untested
    the reporting administrator has only exercised Positron Pro sessions.

Possibly related

Impact

An enterprise deployment where all outbound traffic is forced through a corporate proxy that denies
internal hosts. With a self-hosted extension gallery on such a host, no user can install any
extension from the Extensions pane; the administrator's available options are all outside the
product (proxy allowlist change, or pre-installing extensions from .vsix). The extension gallery in
this case is housed in Posit Package Manager.

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions