Description
When a GitHub Actions self-hosted runner is configured with HTTPS_PROXY, the upload-artifact action fails with Error: Bad Request during the blob upload step. The @actions/artifact package creates a BlobClient from @azure/storage-blob without configuring proxy transport, causing the Azure SDK's HTTP pipeline to handle the CONNECT tunnel incorrectly.
Root cause
In packages/artifact/src/internal/upload/blob-upload.ts:
const blobClient = new BlobClient(authenticatedUploadURL)
const blockBlobClient = blobClient.getBlockBlobClient()
The BlobClient is created with only a URL — no StoragePipelineOptions with proxy configuration. The @azure/storage-blob SDK creates its own internal HTTP pipeline which may not correctly handle HTTPS CONNECT tunnels even though HTTPS_PROXY is set in the environment.
Suggested fix
Pass pipeline options with proxy configuration:
import { ProxyPolicy } from '@azure/core-rest-pipeline'
const blobClient = new BlobClient(authenticatedUploadURL, undefined, {
proxyOptions: {
host: process.env.HTTPS_PROXY || process.env.https_proxy,
}
})
Or use newPipeline with the StorageSharedKeyCredential and configure the proxy in the pipeline options.
Evidence
Proxy logs confirm the CONNECT tunnel succeeds but the upload stalls:
CONNECT productionresultssa6.blob.core.windows.net:443 → status=200 (ALLOWED)
requestSize=17259 (only ~17 KB sent)
responseSize=8828 (error response)
latency=74.999634s (stalled for 75 seconds)
user-agent: azsdk-net-Storage.Blobs/12.27.0 (.NET 8.0.25; Ubuntu 24.04.4 LTS)
Curl and Python upload 1 MB to the same blob endpoint through the same proxy in <1 second:
curl PUT 1MB to blob → HTTP 409 in 0.3s
Python PUT 1MB to blob → HTTP 409 in 0.6s
Standalone .NET HttpClient.PutAsync() also works fine (1.1s) — the issue is in how the Azure SDK BlobClient is instantiated without proxy configuration.
Workflow output
Run actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f
With the provided path, there will be 1 file uploaded
Artifact name is valid!
Root directory input is valid!
Uploading artifact: trivy-scan-results--skaffold-build-24347418057-1-1776089109.zip
Beginning upload of artifact content to blob storage
Error: Bad Request
Related
- actions/runner#4351 — same root cause in the runner's .NET
ResultsHttpClient.cs which also creates BlobClient without proxy transport. The runner's step log/summary/diagnostic uploads stall in the same way.
Environment
- Runner:
GitHubActionsRunner-linux-x64/2.333.1 (self-hosted on GKE via actions-runner-controller v0.13.1)
- Action:
actions/upload-artifact@v4 (SHA bbbca2ddaa5d8feaa63e36b76fdaad77386f024f)
- Package:
@actions/artifact v6.2.0 → @azure/storage-blob v12.30.0
- Proxy: GCP Secure Web Proxy (HTTPS forward proxy, CONNECT tunnel, no TLS inspection)
- Config:
HTTPS_PROXY=http://<proxy-ip>:443
- Platform: Ubuntu 24.04.4 LTS on GKE
Workaround
Add .blob.core.windows.net to NO_PROXY:
NO_PROXY=.blob.core.windows.net
This bypasses the proxy for Azure Blob traffic, but defeats the purpose of hostname-based egress control through the proxy.
Description
When a GitHub Actions self-hosted runner is configured with
HTTPS_PROXY, theupload-artifactaction fails withError: Bad Requestduring the blob upload step. The@actions/artifactpackage creates aBlobClientfrom@azure/storage-blobwithout configuring proxy transport, causing the Azure SDK's HTTP pipeline to handle the CONNECT tunnel incorrectly.Root cause
In
packages/artifact/src/internal/upload/blob-upload.ts:The
BlobClientis created with only a URL — noStoragePipelineOptionswith proxy configuration. The@azure/storage-blobSDK creates its own internal HTTP pipeline which may not correctly handle HTTPS CONNECT tunnels even thoughHTTPS_PROXYis set in the environment.Suggested fix
Pass pipeline options with proxy configuration:
Or use
newPipelinewith theStorageSharedKeyCredentialand configure the proxy in the pipeline options.Evidence
Proxy logs confirm the CONNECT tunnel succeeds but the upload stalls:
Curl and Python upload 1 MB to the same blob endpoint through the same proxy in <1 second:
Standalone .NET HttpClient.PutAsync() also works fine (1.1s) — the issue is in how the Azure SDK BlobClient is instantiated without proxy configuration.
Workflow output
Related
ResultsHttpClient.cswhich also createsBlobClientwithout proxy transport. The runner's step log/summary/diagnostic uploads stall in the same way.Environment
GitHubActionsRunner-linux-x64/2.333.1(self-hosted on GKE via actions-runner-controller v0.13.1)actions/upload-artifact@v4(SHAbbbca2ddaa5d8feaa63e36b76fdaad77386f024f)@actions/artifactv6.2.0 →@azure/storage-blobv12.30.0HTTPS_PROXY=http://<proxy-ip>:443Workaround
Add
.blob.core.windows.nettoNO_PROXY:This bypasses the proxy for Azure Blob traffic, but defeats the purpose of hostname-based egress control through the proxy.