{"record":{"id":"1aeeb18962faf8ca","repo":"janhq/jan","slug":"failed-to-fetch-file-response-statustext","errorCode":null,"errorMessage":"Failed to fetch file: ${response.statusText}","messagePattern":"Failed to fetch file: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"web-app/src/containers/ChatInput.tsx","lineNumber":1475,"sourceCode":"              extensions: ['jpg', 'jpeg', 'png'],\n            },\n          ],\n        })\n\n        if (selected) {\n          const paths = Array.isArray(selected) ? selected : [selected]\n          const files: File[] = []\n\n          for (const path of paths) {\n            try {\n              // Use Tauri's convertFileSrc to create a valid URL for the file\n              const { convertFileSrc } = await import('@tauri-apps/api/core')\n              const fileUrl = convertFileSrc(path)\n\n              // Fetch the file as blob\n              const response = await fetch(fileUrl)\n              if (!response.ok) {\n                throw new Error(`Failed to fetch file: ${response.statusText}`)\n              }\n\n              const blob = await response.blob()\n              const fileName =\n                path.split(/[\\\\/]/).filter(Boolean).pop() || 'image'\n              const ext = fileName.toLowerCase().split('.').pop()\n              const mimeType =\n                ext === 'png'\n                  ? 'image/png'\n                  : ext === 'jpg' || ext === 'jpeg'\n                    ? 'image/jpeg'\n                    : 'image/jpeg'\n\n              const file = new File([blob], fileName, { type: mimeType })\n              files.push(file)\n            } catch (error) {\n              console.error('Failed to read file:', error)\n              toast.error('Failed to read file', {","sourceCodeStart":1457,"sourceCodeEnd":1493,"githubUrl":"https://github.com/janhq/jan/blob/fad3f12a147d138388a66f0d92a02b2675f65294/web-app/src/containers/ChatInput.tsx#L1457-L1493","documentation":"Same Tauri asset-protocol fetch pattern as the video case, but for image picking. The message includes the literal `response.statusText` so it is more debuggable than error 120, but it still surfaces an opaque custom-protocol string when the asset URL fetch is non-OK. The error is caught per-file inside the loop and shown as a toast without aborting the whole batch.","triggerScenarios":"Picking one or more images via `serviceHub.dialog().open({ filters: [{ name: 'Images', extensions: ['jpg','jpeg','png'] }] })`, then `fetch(convertFileSrc(path))` returning non-OK. Same triggers as 120: scope mismatch, deleted file, permission, or a non-image path slipping through the filter.","commonSituations":"Asset scope missing the picked directory; symlink/junction paths not covered by scope globs; the user picked a file whose extension matched but content is unreadable; cross-platform path normalization (forward vs back slashes) producing a bad asset URL.","solutions":["Confirm `security.assetProtocol.scope` covers the user's picture folders.","Log `response.status` not just `statusText`, since Tauri frequently sends empty statusText.","Skip the failing file and continue the batch (already partially done) but report the specific filename in the toast.","Pre-check `path` validity via Tauri fs `exists()` before converting."],"exampleFix":"// before\nif (!response.ok) {\n  throw new Error(`Failed to fetch file: ${response.statusText}`)\n}\n\n// after\nif (!response.ok) {\n  throw new Error(\n    `Failed to fetch file ${fileName}: HTTP ${response.status}${\n      response.statusText ? ` ${response.statusText}` : ''\n    }`\n  )\n}","handlingStrategy":"try-catch","validationCode":"const { exists } = await import('@tauri-apps/plugin-fs')\nif (!(await exists(path))) {\n  toast.error('Image file no longer exists', { description: path })\n  continue\n}","typeGuard":"function isReadableImageResponse(res: Response): res is Response & { ok: true } {\n  return res.ok && (res.headers.get('content-type')?.startsWith('image/') ?? true)\n}","tryCatchPattern":"try {\n  const response = await fetch(fileUrl)\n  if (!response.ok) {\n    toast.error('Could not read image', {\n      description: `HTTP ${response.status} for ${fileName}`,\n    })\n    continue\n  }\n  // ... build File\n} catch (error) {\n  toast.error('Failed to read file', {\n    description: error instanceof Error ? error.message : String(error),\n  })\n}","preventionTips":["Cover picked directories in the asset scope.","Include HTTP status in the surfaced message; Tauri statusText is unreliable.","Report the specific failing filename so the user knows which image to retry."],"tags":["tauri","asset-protocol","file-io","image"],"backgroundTag":null,"analyzedSha":"fad3f12a147d138388a66f0d92a02b2675f65294","analyzedAt":"2026-08-12T20:33:47.516Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}