{"record":{"id":"e7d4d04b2ff42534","repo":"infiniflow/ragflow","slug":"failed-to-download-file-path-response-status","errorCode":null,"errorMessage":"Failed to download ${file.path}: ${response.status} ${response.statusText}","messagePattern":"Failed to download (.+?): (.+?) (.+?)","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"web/src/pages/skills/components/upload-modal.tsx","lineNumber":514,"sourceCode":"\n      // If download_url is not provided, construct raw URL\n      if (!downloadUrl) {\n        if (platform === 'github') {\n          // https://raw.githubusercontent.com/owner/repo/ref/path\n          downloadUrl = `${config.rawBase}/${owner}/${repo}/${ref}/${file.path}`;\n        } else if (platform === 'gitee') {\n          // https://gitee.com/owner/repo/raw/ref/path\n          downloadUrl = `${config.rawBase}/${owner}/${repo}/raw/${ref}/${file.path}`;\n        }\n      }\n\n      if (!downloadUrl) {\n        throw new Error(`Download URL not available for file: ${file.path}`);\n      }\n\n      const response = await fetch(downloadUrl);\n      if (!response.ok) {\n        throw new Error(\n          `Failed to download ${file.path}: ${response.status} ${response.statusText}`,\n        );\n      }\n\n      const blob = await response.blob();\n      const fileName = file.path.split('/').pop() || 'file';\n\n      // Use MIME type from extension if blob.type is empty or generic\n      let fileType = blob.type;\n      if (\n        !fileType ||\n        fileType === 'application/octet-stream' ||\n        fileType === 'text/plain'\n      ) {\n        fileType = getMimeTypeFromExtension(file.path);\n      }\n\n      const downloadedFile = new File([blob], fileName, {","sourceCodeStart":496,"sourceCodeEnd":532,"githubUrl":"https://github.com/infiniflow/ragflow/blob/554fb1133ac3861732235ad9c377eb5e0a770665/web/src/pages/skills/components/upload-modal.tsx#L496-L532","documentation":"Raised when the raw file download (raw.githubusercontent.com or gitee.com/.../raw/...) returns a non-ok HTTP status. Includes the status code and statusText for diagnosis. Common causes: the file was removed between listing and download, the ref moved, the URL requires authentication (private repo), or rate limiting on the raw endpoint.","triggerScenarios":"fetch(downloadUrl) for a listed file returns 404 (file deleted/renamed since the listing), 401/403 (private repo, raw URL needs token which is not attached to raw fetches), or 429 rate limit.","commonSituations":"Repo actively changing during import; private repository imported with a token that authenticates the API but not the raw fetch; large repos hitting raw CDN limits.","solutions":["Retry the import — transient 403/429 from raw endpoints often clear","For private repos, fetch content via the API's download_url (which includes a signed token) or the contents API base64 payload instead of constructing raw URLs","Verify the file still exists at the given ref in the browser","Check the status code in the message: 404 = file gone, 403 = auth/rate, 5xx = upstream issue"],"exampleFix":"// before\nconst response = await fetch(downloadUrl);\nif (!response.ok) {\n  throw new Error(`Failed to download ${file.path}: ${response.status} ${response.statusText}`);\n}\n\n// after\nconst response = await fetch(downloadUrl, token ? { headers: { Authorization: `Bearer ${token}` } } : undefined);\nif (!response.ok) {\n  throw new Error(`Failed to download ${file.path}: ${response.status} ${response.statusText}`);\n}","handlingStrategy":"retry","validationCode":"// pre-flight: HEAD the raw URL to confirm it resolves\nconst probe = await fetch(downloadUrl, { method: 'HEAD' });\nif (!probe.ok) {\n  skipOrWarn(`File unreachable (${probe.status}): ${file.path}`);\n}","typeGuard":"function isTransientHttpError(status: number): boolean {\n  return status === 429 || status >= 500;\n}","tryCatchPattern":"for (let attempt = 0; attempt < 3; attempt++) {\n  const res = await fetch(downloadUrl);\n  if (res.ok) { /* use blob */ break; }\n  if (!isTransientHttpError(res.status)) {\n    throw new Error(`Failed to download ${file.path}: ${res.status} ${res.statusText}`);\n  }\n  await new Promise((r) => setTimeout(r, 2 ** attempt * 500)); // backoff, then retry\n}","preventionTips":["Attach the Authorization header to raw fetches when importing private repos","Prefer the API's download_url (often signed) over hand-built raw URLs","Throttle concurrent downloads to avoid raw-endpoint rate limits"],"tags":["download","http-error","github-api","network","skills"],"backgroundTag":null,"analyzedSha":"554fb1133ac3861732235ad9c377eb5e0a770665","analyzedAt":"2026-08-15T09:20:16.380Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}