jitsi/jitsi-meet · warning
Could not download file:
Error message
Could not download file:
What it means
A logger.warn in the file-sharing web middleware: downloading a shared file via its presigned URL failed. The promise chain from the download attempt (fetch/save of the presigned URL) rejects, the catch logs 'Could not download file:' with the underlying error, and a sticky 'download failed' notification is dispatched.
Source
Thrown at react/features/file-sharing/middleware.web.ts:248
'Authorization': `Bearer ${token}`
}
}))
.then((response: any) => response.json())
.then((data: { fileName: string; presignedUrl: string; }) => {
const { presignedUrl, fileName } = data;
if (!presignedUrl) {
throw new Error('No presigned URL found in the response.');
}
store.dispatch(showNotification({
titleKey: 'fileSharing.downloadStarted'
}, NOTIFICATION_TIMEOUT_TYPE.SHORT));
return downloadFile(presignedUrl, fileName);
})
.catch((error: any) => {
logger.warn('Could not download file:', error);
store.dispatch(showErrorNotification({
titleKey: 'fileSharing.downloadFailedTitle',
descriptionKey: 'fileSharing.downloadFailedDescription',
appearance: NOTIFICATION_TYPE.ERROR
}, NOTIFICATION_TIMEOUT_TYPE.STICKY));
});
return next(action);
}
case _FILE_REMOVED: {
const result = next(action);
const state = store.getState();
const { messages } = state['features/chat'];
// Find the message corresponding to this file and mark it as deleted.
const fileMessage = messages.find(msg => msg.messageId === action.fileId);View on GitHub (pinned to 98de6219cc)
Solutions
- Check the logged error object: 403 signature/expiry means expired presigned URL — re-request a fresh URL from the file-sharing backend and retry; network/CORS errors point to storage config
- Verify CORS configuration on the file storage allows GET from the deployment origin
- Confirm the file still exists server-side and the sharing service issues URLs with adequate TTL
- Add a retry with a freshly minted presigned URL before surfacing the sticky failure notification
Example fix
// before
return downloadFile(presignedUrl, fileName);
// after
return downloadFile(presignedUrl, fileName).catch(async error => {
logger.warn('Could not download file:', error);
if (error?.status === 403 || error?.name === 'AbortError') {
const fresh = await fetchNewPresignedUrl(fileUrl); // backend re-sign
return downloadFile(fresh, fileName);
}
throw error;
}); Defensive patterns
Strategy: retry
Validate before calling
// Check URL freshness before downloading (presigned URLs carry Expires param)
const isFresh = (url: string): boolean => {
const expires = new URL(url).searchParams.get('Expires');
return !expires || Number(expires) * 1000 > Date.now() + 30_000;
};
if (!isFresh(presignedUrl)) {
presignedUrl = await refreshPresignedUrl(fileName);
} Type guard
const isDownloadError = (e: unknown): e is { status: number; message: string } =>
typeof e === 'object' && e !== null && 'status' in e; Try / catch
.catch((error: unknown) => {
logger.warn('Could not download file:', error);
if (isDownloadError(error) && (error.status === 403 || error.status === 410)) {
return refreshAndRetryDownload(fileName); // get fresh presigned URL
}
store.dispatch(showErrorNotification({
titleKey: 'fileSharing.downloadFailedTitle',
descriptionKey: 'fileSharing.downloadFailedDescription'
}, NOTIFICATION_TIMEOUT_TYPE.STICKY));
}) Prevention
- Issue presigned URLs with TTLs that comfortably exceed expected share lifetime
- Refresh/re-sign URLs lazily at click time rather than embedding long-lived ones in messages
- Configure CORS on the object store for GET from the app origin
- Retry transient network failures once with a fresh URL before showing the sticky error
When it happens
Trigger: The presigned URL is expired or revoked (S3/URL TTL passed), the storage backend is unreachable, CORS blocks the GET from the browser, the file was deleted server-side, or a network failure/abort occurs during fetch.
Common situations: User clicks a share link long after upload and the presigned URL expired; file-sharing storage (S3/other) misconfigured or bucket deleted; missing CORS headers on the object store; reverse proxy timeouts on large files; flaky client network.
AI-assisted analysis of jitsi/jitsi-meet@98de6219cc (2026-08-28).
Data as JSON: /api/errors/e4ba95762d7f1386.
Report an issue: GitHub.