{"record":{"id":"4b93116277584dff","repo":"danny-avila/LibreChat","slug":"no-files-provided-for-download","errorCode":null,"errorMessage":"No files provided for download","messagePattern":"No files provided for download","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"client/src/hooks/Files/useSharePointDownload.ts","lineNumber":37,"sourceCode":"export default function useSharePointDownload({\n  onFilesDownloaded,\n  onError,\n}: UseSharePointDownloadProps = {}): UseSharePointDownloadReturn {\n  const { showToast } = useToastContext();\n  const [downloadProgress, setDownloadProgress] = useState<SharePointBatchProgress | null>(null);\n  const [error, setError] = useState<string | null>(null);\n\n  const { token, refetch: refetchToken } = useSharePointToken({\n    enabled: false,\n    purpose: 'Download',\n  });\n\n  const batchDownloadMutation = useSharePointBatchDownload();\n\n  const downloadSharePointFiles = useCallback(\n    async (files: SharePointFile[]): Promise<File[]> => {\n      if (!files || files.length === 0) {\n        throw new Error('No files provided for download');\n      }\n\n      setError(null);\n      setDownloadProgress({ completed: 0, total: files.length, failed: [] });\n\n      try {\n        let accessToken = token?.access_token;\n        if (!accessToken) {\n          showToast({\n            message: 'Getting SharePoint access token...',\n            status: 'info',\n            duration: 2000,\n          });\n\n          const tokenResult = await refetchToken();\n          accessToken = tokenResult.data?.access_token;\n\n          if (!accessToken) {","sourceCodeStart":19,"sourceCodeEnd":55,"githubUrl":"https://github.com/danny-avila/LibreChat/blob/5ff282f9006c436e561de1afd39a481bea1ef0d8/client/src/hooks/Files/useSharePointDownload.ts#L19-L55","documentation":"Thrown by the SharePoint download hook's `downloadSharePointFiles` callback when the `files` array is empty or null. It is a pure precondition guard that fires before any token fetch or network call, so no Graph request is made. The hook tracks progress via `setDownloadProgress` and this throw prevents a meaningless zero-file batch.","triggerScenarios":"A UI action triggers a download with an empty selection — e.g. the user opened the file picker, deselected everything, and clicked download; or a parent component passed `selectedFiles` before the picker populated it.","commonSituations":"Download button enabled with zero selected files; a race where the picker's `onSelect` hasn't populated the array yet; filtering logic that emptied the list (e.g. removing already-downloaded items) before the call; default-export misuse where the files prop is undefined.","solutions":["Disable the download button when `files.length === 0`.","If the call can race with selection, guard at the call site: `if (files.length) await downloadSharePointFiles(files)`.","Ensure the picker actually returns a non-empty array before invoking the hook."],"exampleFix":"// before\n<Button onClick={() => downloadSharePointFiles(files)} />;\n// after — disable until at least one file is selected\n<Button disabled={files.length === 0} onClick={() => downloadSharePointFiles(files)} />;","handlingStrategy":"validation","validationCode":"// Validate the selection before invoking the hook's callback\nif (!files || files.length === 0) throw new Error('Select at least one file to download');","typeGuard":"function isNonEmptyFileList(files: unknown): files is SharePointFile[] {\n  return Array.isArray(files) && files.length > 0;\n}","tryCatchPattern":"// Disable the download action for empty selections\n<Button disabled={!files?.length} onClick={() => downloadSharePointFiles(files)} />","preventionTips":["Disable download UI when the selection is empty.","Filter the list (e.g. remove already-downloaded) only after confirming the result is non-empty.","Guard at the call site rather than relying solely on the internal throw."],"tags":["react","sharepoint","validation","file-download","client"],"backgroundTag":null,"analyzedSha":"5ff282f9006c436e561de1afd39a481bea1ef0d8","analyzedAt":"2026-08-12T21:38:08.145Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}