janhq/jan · error · Error
Failed to fetch file: ${response.statusText}
Error message
Failed to fetch file: ${response.statusText} What it means
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.
Source
Thrown at web-app/src/containers/ChatInput.tsx:1475
extensions: ['jpg', 'jpeg', 'png'],
},
],
})
if (selected) {
const paths = Array.isArray(selected) ? selected : [selected]
const files: File[] = []
for (const path of paths) {
try {
// Use Tauri's convertFileSrc to create a valid URL for the file
const { convertFileSrc } = await import('@tauri-apps/api/core')
const fileUrl = convertFileSrc(path)
// Fetch the file as blob
const response = await fetch(fileUrl)
if (!response.ok) {
throw new Error(`Failed to fetch file: ${response.statusText}`)
}
const blob = await response.blob()
const fileName =
path.split(/[\\/]/).filter(Boolean).pop() || 'image'
const ext = fileName.toLowerCase().split('.').pop()
const mimeType =
ext === 'png'
? 'image/png'
: ext === 'jpg' || ext === 'jpeg'
? 'image/jpeg'
: 'image/jpeg'
const file = new File([blob], fileName, { type: mimeType })
files.push(file)
} catch (error) {
console.error('Failed to read file:', error)
toast.error('Failed to read file', {View on GitHub (pinned to fad3f12a14)
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.
Example fix
// before
if (!response.ok) {
throw new Error(`Failed to fetch file: ${response.statusText}`)
}
// after
if (!response.ok) {
throw new Error(
`Failed to fetch file ${fileName}: HTTP ${response.status}${
response.statusText ? ` ${response.statusText}` : ''
}`
)
} Defensive patterns
Strategy: try-catch
Validate before calling
const { exists } = await import('@tauri-apps/plugin-fs')
if (!(await exists(path))) {
toast.error('Image file no longer exists', { description: path })
continue
} Type guard
function isReadableImageResponse(res: Response): res is Response & { ok: true } {
return res.ok && (res.headers.get('content-type')?.startsWith('image/') ?? true)
} Try / catch
try {
const response = await fetch(fileUrl)
if (!response.ok) {
toast.error('Could not read image', {
description: `HTTP ${response.status} for ${fileName}`,
})
continue
}
// ... build File
} catch (error) {
toast.error('Failed to read file', {
description: error instanceof Error ? error.message : String(error),
})
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- response.statusText
- Failed to decompress archive: ${String(e)}
- ${String(e)}
- ${e}
- Failed to load llamacpp backend
AI-assisted analysis of janhq/jan@fad3f12a14 (2026-08-12).
Data as JSON: /api/errors/1aeeb18962faf8ca.
Report an issue: GitHub.