Repository navigation
Azure Blob: image/file download fails on private containers (400 Failed to download image) #13340
Description
What happened
With fileStrategy: azure_blob and a storage account where anonymous public blob access is disabled at the account level, uploading an image to a vision-capable model fails:
400 Failed to download image from https://<account>.blob.core.windows.net/<container>/images/<userId>/<file>.webp
File preview/download from "My Files" fails the same way.
Root cause
For azure_blob/s3 sources, encodeAndFormat (api/server/services/Files/images/encode.js) downloads the blob server-side and base64-encodes it (the blob URL is never sent to the model). For Azure that download goes through getAzureFileStream (api/server/services/Files/Azure/crud.js), which makes an unauthenticated request:
async function getAzureFileStream(_req, fileURL) {
const response = await axios({ method: 'get', url: fileURL, responseType: 'stream' });
return response.data;
}On a private container this returns 403, or 409 PublicAccessNotPermitted when the account disallows public access entirely. By contrast, S3's equivalent getS3FileStream uses the authenticated SDK (GetObjectCommand), so S3 works with private buckets — Azure is the inconsistent one here.
Steps to reproduce
- Azure Storage account with "Allow Blob anonymous access" disabled (the default for secured accounts).
fileStrategy: azure_blob,AZURE_STORAGE_PUBLIC_ACCESS=false.- Upload an image and send it to a vision-capable model →
400 Failed to download image. - (Same root cause: downloading/previewing any uploaded file from "My Files".)
file_search/RAG over documents is unaffected, because the file is embedded at upload time from the local temp file, not re-fetched from the blob.
Expected behavior
Private Azure Blob containers should work, the same way S3 private buckets do.
Proposed fix
PR #13245 ("Azure Blob Storage URL Signing and Refresh Support") addresses this comprehensively via SAS URL signing, and is the preferred direction since it also covers cases where the blob URL is consumed directly by the client.
A minimal alternative (or complement) for the server-side download path is to make getAzureFileStream download through the authenticated SDK client, mirroring getS3FileStream:
async function getAzureFileStream(_req, fileURL) {
try {
const containerClient = await getAzureContainerClient(AZURE_CONTAINER_NAME);
const rawPath = fileURL.split(`${AZURE_CONTAINER_NAME}/`)[1];
if (!rawPath) {
throw new Error('Blob path could not be derived from the file URL');
}
const blobPath = decodeURIComponent(rawPath.split('?')[0]);
const blockBlobClient = containerClient.getBlockBlobClient(blobPath);
const downloadResponse = await blockBlobClient.download();
return downloadResponse.readableStreamBody;
} catch (error) {
logger.error('[getAzureFileStream] Error getting blob stream:', error);
throw error;
}
}This keeps blobs fully private (nothing exposed to the model or anonymously) and works whether or not the container is public, since authenticated access is a superset.
Environment
- LibreChat v0.7.9 (bug also present on
main) - File storage: Azure Blob (
azure_blob), connection-string auth - Storage account: anonymous public blob access disabled
Activity
rahepler2 commentedon May 30, 2026
I just added a PR to address this. WOuld love your feedback on it: #13411
JorgeCosta87 commentedon Jun 1, 2026
A fix is in flight via #13411 (authenticated SDK + proxy) and #13245 (SAS signing). See the breakdown in #13411 — the two approaches are complementary: SAS-direct for zero-bandwidth client fetches, authenticated SDK + proxy for a no-bearer-token /
Storage Blob Data Reader-only deployment. The shared server-sidegetAzureFileStreamchange (unauthenticatedaxios.get→ authenticated SDK) is what closes the vision + preview/download path this issue describes.fix: authenticated Azure Blob download for private containers