{"record":{"id":"ce1d1c0742dadb0e","repo":"jd-opensource/joyagent-jdgenie","slug":"network-response-was-not-ok","errorCode":null,"errorMessage":"Network response was not ok","messagePattern":"Network response was not ok","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"ui/src/components/ActionPanel/FileRenderer.tsx","lineNumber":47,"sourceCode":" * @param data 文件内容\n * @returns 格式化后的文件内容\n */\nconst formatFileContent = (ext: string | undefined, data: string | undefined): string => {\n  if (ext === 'md' || ext === 'txt') {\n    return data || '';\n  }\n  return `\\`\\`\\`${ext}\\n${data || ''}\\n\\`\\`\\``;\n};\n\nconst FileRenderer: GenieType.FC<FileRendererProps> = React.memo((props) => {\n  const { fileUrl, fileName, className } = props;\n\n  const ext = useMemo(() => getFileExtension(fileName), [fileName]);\n\n  const { data, loading, error } = useRequest(async () => {\n    const response = await fetch(fileUrl);\n    if (!response.ok) {\n      throw new Error('Network response was not ok');\n    }\n    return await response.text();\n  }, { refreshDeps: [fileUrl] });\n\n  const markStr = useMemo(() => formatFileContent(ext, data), [ext, data]);\n\n  if (loading) {\n    return <Loading className={LOADING_CLASS} />;\n  }\n\n  if (error) {\n    return (\n      <Alert\n        type=\"error\"\n        message=\"加载失败\"\n        description={error.message}\n        showIcon\n        className={ERROR_CLASS}","sourceCodeStart":29,"sourceCodeEnd":65,"githubUrl":"https://github.com/jd-opensource/joyagent-jdgenie/blob/2417e0b8b636d941ad5fb14c59b20dddfef5375d/ui/src/components/ActionPanel/FileRenderer.tsx#L29-L65","documentation":"FileRenderer fetches fileUrl and explicitly throws Error('Network response was not ok') when response.ok is false (any non-2xx status). useRequest captures it into `error`, which the component should render. It signals the file content could not be downloaded.","triggerScenarios":"The file URL returns 404 (file deleted or expired), 401/403 (auth missing/expired presigned URL), or 500 from the file server; CORS-blocked or unreachable host also yields failed fetches.","commonSituations":"Expired pre-signed S3/MinIO links; files removed after upload; wrong fileUrl built by the backend; viewer opened before the file finished uploading.","solutions":["Verify fileUrl is correct and the file still exists on the server","Re-generate the presigned/authenticated URL and retry the fetch","Render the useRequest `error` state with a retry button instead of blank content","Check server logs for the failing status code","Handle 401/403 by refreshing credentials before fetching"],"exampleFix":"// before\nconst res = await fetch(fileUrl);\nif (!res.ok) throw new Error('Network response was not ok');\n// after\nconst res = await fetch(fileUrl);\nif (!res.ok) {\n  throw new Error(`Failed to load file (HTTP ${res.status}): ${fileUrl}`);\n}\nreturn await res.text();","handlingStrategy":"try-catch","validationCode":"// before rendering, verify the URL is reachable\nasync function checkUrl(url: string): Promise<boolean> {\n  try {\n    const res = await fetch(url, { method: 'HEAD' });\n    return res.ok;\n  } catch { return false; }\n}","typeGuard":"function isHttpOk(res: Response): boolean {\n  return res.ok && res.status >= 200 && res.status < 300;\n}","tryCatchPattern":"const { data, error, run } = useRequest(() => loadFile(fileUrl), {\n  refreshDeps: [fileUrl],\n  onError: (e) => message.error(`文件加载失败：${e.message}`),\n});\nif (error) return <Button onClick={run}>重试</Button>;","preventionTips":["Render the useRequest error state instead of assuming data always loads","Refresh presigned URLs before they expire","Add a retry action tied to fileUrl changes","Validate file existence at list time and hide dead entries"],"tags":["react","fetch","network","typescript"],"backgroundTag":"http-error-response","analyzedSha":"2417e0b8b636d941ad5fb14c59b20dddfef5375d","analyzedAt":"2026-09-08T11:28:19.414Z","contentChangedAt":"2026-09-08T11:28:19.414Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}