immich-app/immich · error · Error
errors.unable_to_upload_file
Error message
errors.unable_to_upload_file
What it means
In the web app's file uploader, after POSTing the asset to /assets, any HTTP status other than 200/201 throws a localized error ('errors.unable_to_upload_file'). This signals the server rejected the upload (validation failure, auth issue, duplicate handling error, etc.) for that specific device asset.
Solutions
- Check the response status/body (network tab) to identify the underlying rejection (401 vs 413 vs 500).
- For 413: raise the reverse proxy body-size limit (e.g. nginx client_max_body_size) above your largest photo/video.
- For 401: re-authenticate (log in again) and retry the upload.
- For 500: check immich-server logs (disk full? database error?) and free space or fix storage config.
- Retry failed assets from the upload queue after fixing the cause.
Example fix
// nginx before client_max_body_size 1m; // after client_max_body_size 50000M;
Defensive patterns
Strategy: try-catch
Validate before calling
if (!navigator.onLine) throw new OfflineError('Defer upload until online');
if (file.size === 0) throw new ValidationError('Refusing to upload empty file'); Type guard
const isUploadFailure = (e: unknown): boolean =>
e instanceof Error && e.message === $t('errors.unable_to_upload_file'); Try / catch
try { await fileUploader(file); } catch (e) { if (isUploadFailure(e)) { uploadAssetsStore.markFailed(deviceAssetId); showToast(t('errors.unable_to_upload_file')); } else throw e; } Prevention
- Set reverse-proxy client_max_body_size above your largest media file
- Keep sessions alive during long uploads (re-auth before large batches)
- Monitor server disk space; full disks surface as 500s during upload
- Show per-asset upload status so failures can be retried individually
When it happens
Trigger: fileUploader receives an axios response from the /assets endpoint with a status outside {200, 201} — e.g. 401 (expired session), 413 (payload too large via reverse proxy), 4xx/5xx server errors.
Common situations: Nginx/proxy client_max_body_size smaller than the photo; session expiring mid-upload; server storage full or returning 500; uploading zero-byte or corrupted files the API rejects.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- await response.text()
- EOFException
- Failed to fetch activation key
- Machine learning request to
- Server is not reachable
AI-assisted analysis of immich-app/immich@e55ac299a4 (2026-09-15).
Data as JSON: /api/errors/50b994ac3ad84c35.
Report an issue: GitHub.
Appendix: source
Thrown at web/src/lib/utils/file-uploader.ts:210
};
}
} catch (error) {
console.error(`Error calculating sha1 file=${assetFile.name})`, error);
}
}
if (!responseData) {
const queryParams = asQueryString(authManager.params);
uploadAssetsStore.updateItem(deviceAssetId, { message: $t('asset_uploading') });
const response = await uploadRequest<AssetMediaResponseDto>({
url: getBaseUrl() + '/assets' + (queryParams ? `?${queryParams}` : ''),
data: formData,
onUploadProgress: (event) => uploadAssetsStore.updateProgress(deviceAssetId, event.loaded, event.total),
});
if (![200, 201].includes(response.status)) {
throw new Error($t('errors.unable_to_upload_file'));
}
responseData = response.data;
}
if (responseData.status === AssetMediaStatus.Duplicate) {
uploadAssetsStore.track('duplicate');
} else {
uploadAssetsStore.track('success');
}
if (albumId && !authManager.isSharedLink) {
uploadAssetsStore.updateItem(deviceAssetId, { message: $t('asset_adding_to_album') });
await addAssetsToAlbums([albumId], [responseData.id], { notify: false });
uploadAssetsStore.updateItem(deviceAssetId, { message: $t('asset_added_to_album') });
}
uploadAssetsStore.updateItem(deviceAssetId, {View on GitHub (pinned to e55ac299a4)