janhq/jan · error
Failed to ingest file
Error message
Failed to ingest file
What it means
In project-uploads-store, after await ingestFn(projectId, att) resolves, the store requires result.id to build its progress state; if the UploadResult has no id it throws 'Failed to ingest file'. This wraps any ingestion whose result lacked an identifier (including the upstream 'Failed to resolve ingested attachment id' path producing an id-less result).
Solutions
- Fix the upstream service so it throws instead of returning an id-less UploadResult when the extension yields no file id
- Validate result.id in the store and surface the underlying cause (log res/files) rather than a generic message
- If using a placeholder/mock ingestFn, make it return { id: ulid() } like DefaultUploadsService.ingestImage does
Example fix
// before
const result = await ingestFn(projectId, att)
if (!result.id) throw new Error('Failed to ingest file')
// after
const result = await ingestFn(projectId, att)
if (!result?.id) {
throw new Error(`Failed to ingest file ${att.name}: ingestion returned no id`)
} Defensive patterns
Strategy: validation
Validate before calling
if (!result || typeof result.id !== 'string') throw new Error(`Ingestion for ${att.name} returned no id`) Type guard
const hasUploadId = (r: UploadResult | undefined): r is UploadResult & { id: string } =>
typeof r?.id === 'string' && r.id.length > 0 Try / catch
try {
const result = await ingestFn(projectId, att)
if (!hasUploadId(result)) throw new Error(`Failed to ingest ${att.name}: no id returned`)
// proceed with progress update
} catch (e) {
setUploadFailed(projectId, att, e instanceof Error ? e.message : String(e))
throw e
} Prevention
- Make ingest services throw on unusable responses instead of returning id-less UploadResults
- Add a store-level invariant test that every successful ingestion path yields a string id
- Keep mock/placeholder ingest implementations returning real ulid ids so store logic is exercised identically
When it happens
Trigger: ingestFn (ingestFileAttachmentForProject or equivalent) resolves successfully but its returned object has no id — e.g. the RAG extension returned no files so the service result was constructed without an id, or ingestFn itself returned an id-less UploadResult.
Common situations: RAG extension/backend returned an empty files array; a mocked or placeholder ingest implementation returned { } or { id: undefined }; a swallowed upstream error was converted into a partial result object.
Understand the failure class
Background: EmptyResultError / "no results found": when an API or scraper succeeds but returns zero rows — this error's family across 9 libraries.
Related errors
- Failed to resolve ingested attachment id
- Failed to save store
- File ' ' has already been attached to this thread
- No MLX model files found in repository
AI-assisted analysis of janhq/jan@7205d770c1 (2026-09-17).
Data as JSON: /api/errors/e99a1610842ff150.
Report an issue: GitHub.
Appendix: source
Thrown at web-app/src/stores/project-uploads-store.ts:77
currentFileName = att.name
set((s) => {
const existing = s.progress[projectId]
if (!existing) return s
return {
progress: {
...s.progress,
[projectId]: {
...existing,
current: i,
currentFileName: att.name,
},
},
}
})
const result = await ingestFn(projectId, att)
if (!result.id) throw new Error('Failed to ingest file')
set((s) => {
const existing = s.progress[projectId]
const nextTick = (s.completedTick[projectId] ?? 0) + 1
return {
progress: existing
? {
...s.progress,
[projectId]: { ...existing, current: i + 1 },
}
: s.progress,
completedTick: { ...s.completedTick, [projectId]: nextTick },
}
})
}
handlers.onSuccess()
} catch (error) {
handlers.onError(error, currentFileName)View on GitHub (pinned to 7205d770c1)