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

  1. Fix the upstream service so it throws instead of returning an id-less UploadResult when the extension yields no file id
  2. Validate result.id in the store and surface the underlying cause (log res/files) rather than a generic message
  3. 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

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


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)