{"record":{"id":"a2d75801f7e4b7d1","repo":"infiniflow/ragflow","slug":"failed-to-fetch-message","errorCode":null,"errorMessage":"Failed to fetch: ${message}","messagePattern":"Failed to fetch: (.+?)","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"web/src/pages/skills/components/upload-modal.tsx","lineNumber":392,"sourceCode":"\n      const response = await fetch(url, { headers });\n\n      if (!response.ok) {\n        const errorData = await response.json().catch(() => ({}));\n        const message = errorData.message || `HTTP ${response.status}`;\n\n        if (response.status === 403) {\n          const limit = platform === 'github' ? '60' : '1000';\n          throw new Error(\n            `API rate limit exceeded. ${limit} requests/hour for unauthenticated requests.`,\n          );\n        }\n        if (response.status === 404) {\n          throw new Error(\n            'Repository or path not found. Please check the URL and ensure the repository is public.',\n          );\n        }\n        throw new Error(`Failed to fetch: ${message}`);\n      }\n\n      const items = await response.json();\n      const files: GitFile[] = [];\n\n      // Handle single file case\n      if (!Array.isArray(items)) {\n        if (items.type === 'file') {\n          files.push({\n            path: items.path,\n            download_url: items.download_url,\n            type: 'file',\n            size: items.size,\n          });\n        }\n        return files;\n      }\n","sourceCodeStart":374,"sourceCodeEnd":410,"githubUrl":"https://github.com/infiniflow/ragflow/blob/554fb1133ac3861732235ad9c377eb5e0a770665/web/src/pages/skills/components/upload-modal.tsx#L374-L410","documentation":"The fallback error in the upload modal's fetchGitDirectoryContents for any non-ok response other than 403 and 404. It surfaces the server's own message from the parsed JSON body, or the bare HTTP status when the body is not JSON. Typical statuses behind it: 401 (bad token), 400 (malformed ref), 5xx, or proxy errors.","triggerScenarios":"A token is provided but invalid/expired (401 with GitHub's 'Bad credentials' message); the ref query param is URL-encoded incorrectly (400); GitHub/Gitee outage or upstream 5xx; corporate proxy intercepting the request.","commonSituations":"Revoked or mistyped personal access token; token lacking scopes; GitHub API secondary rate limiting returning unusual status codes; environment where api.github.com is proxied and returns non-JSON errors.","solutions":["Read the embedded `message` in the error — GitHub 401 says 'Bad credentials', indicating the token is wrong/expired","Re-generate or correct the token in the modal's token field and retry","Retry after a short wait for 5xx/transient upstream failures","Verify network egress to api.github.com / gitee.com is not blocked by proxy"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// sanity-check the token shape before using it\nfunction isValidGitToken(token?: string): boolean {\n  return !token || (token.length >= 20 && /^[A-Za-z0-9_]+$/.test(token));\n}\nif (!isValidGitToken(gitToken)) showTokenWarning();","typeGuard":"function isGithubApiError(v: unknown): v is { message: string } {\n  return typeof v === 'object' && v !== null && typeof (v as any).message === 'string';\n}","tryCatchPattern":"try {\n  const items = await fetchGitDirectoryContents(/* ... */);\n} catch (e) {\n  const msg = e instanceof Error ? e.message : String(e);\n  if (msg.includes('Bad credentials')) {\n    setGitValidationMessage('Token is invalid or expired — re-enter it.');\n    return;\n  }\n  if (/HTTP 5\\d\\d/.test(msg)) {\n    await retryAfter(2000); // transient upstream error\n    return;\n  }\n  throw e;\n}","preventionTips":["Use fine-grained tokens with only public-repo read scope and set expiry reminders","Handle 401 'Bad credentials' distinctly from generic failures","Retry transient 5xx with exponential backoff instead of failing the whole import"],"tags":["github-api","http-error","token","network","skills"],"backgroundTag":null,"analyzedSha":"554fb1133ac3861732235ad9c377eb5e0a770665","analyzedAt":"2026-08-15T09:20:16.380Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}