janhq/jan · error
Failed to resolve ingested attachment id
Error message
Failed to resolve ingested attachment id
What it means
After delegating to the RAG extension's ingestAttachments, the service expects a non-empty result.files array whose first entry has an id. If the extension returns no files, an empty array, or a first file without an id, the service cannot determine the ingested attachment's id and throws this error.
Solutions
- Log/res.inspect the raw IngestAttachmentsResult from the RAG extension to see what was actually returned
- Verify the backend ingestion endpoint actually persists the file and returns its id
- Check for a @janhq/core API change in IngestAttachmentsResult (files[].id) and update parsing if the shape moved
Example fix
// before
const res = await ext.ingestAttachments(threadId, [payload])
const result = { id: res.files![0].id }
// after
const res = await ext.ingestAttachments(threadId, [payload])
if (!res?.files?.length || !res.files[0]?.id) {
throw new Error(`Ingestion returned no file id for ${payload.name}: ${JSON.stringify(res)}`)
} Defensive patterns
Strategy: try-catch
Type guard
const hasIngestedFile = (res: IngestAttachmentsResult | undefined): res is IngestAttachmentsResult & { files: [ { id: string } & Record<string, unknown>, ...unknown[] ] } =>
Array.isArray(res?.files) && res.files.length > 0 && typeof res.files[0]?.id === 'string' Try / catch
try {
const result = await uploads.ingestFileAttachment(threadId, att)
return result
} catch (e) {
if (e instanceof Error && e.message === 'Failed to resolve ingested attachment id') {
logIngestionDebug(att, e) // capture raw extension response upstream
throw new Error(`Document ${att.name} was ingested but no file id was returned`)
}
throw e
} Prevention
- Log the raw IngestAttachmentsResult in development to catch backend schema drift early
- Assert IngestAttachmentsResult.files[0].id in integration tests against the real backend
- Treat empty ingestion results as explicit errors in the extension layer with actionable messages
When it happens
Trigger: ext.ingestAttachments resolves with res.files being undefined, an empty array, or files[0] lacking an id field — e.g. the backend accepted the upload but returned no file record.
Common situations: Backend ingestion silently failed or returned a partial response; a @janhq/core version changed IngestAttachmentsResult shape (files renamed or id renamed); the ingested document produced zero stored files.
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 ingest file
- Failed to parse document
- File ' ' exceeds size limit ( bytes > MB).
- IO error
- IoError
AI-assisted analysis of janhq/jan@7205d770c1 (2026-09-17).
Data as JSON: /api/errors/91c2e99895f805ef.
Report an issue: GitHub.
Appendix: source
Thrown at web-app/src/services/uploads/default.ts:30
return { id: ulid() }
}
async ingestFileAttachment(threadId: string, attachment: Attachment): Promise<UploadResult> {
if (attachment.type !== 'document') throw new Error('ingestFileAttachment: attachment is not document')
const ext = ExtensionManager.getInstance().get<RAGExtension>(ExtensionTypeEnum.RAG)
if (!ext?.ingestAttachments) throw new Error('RAG extension not available')
const res: IngestAttachmentsResult = await ext.ingestAttachments(threadId, [
{ path: attachment.path!, name: attachment.name, type: attachment.fileType, size: attachment.size },
])
const files = res.files
if (Array.isArray(files) && files[0]?.id) {
return {
id: files[0].id,
size: typeof files[0].size === 'number' ? Number(files[0].size) : undefined,
chunkCount: typeof files[0].chunk_count === 'number' ? Number(files[0].chunk_count) : undefined,
}
}
throw new Error('Failed to resolve ingested attachment id')
}
async ingestFileAttachmentForProject(projectId: string, attachment: Attachment): Promise<UploadResult> {
if (attachment.type !== 'document') throw new Error('ingestFileAttachmentForProject: attachment is not document')
const ext = ExtensionManager.getInstance().get<RAGExtension>(ExtensionTypeEnum.RAG)
if (!ext?.ingestAttachmentsForProject) throw new Error('RAG extension does not support project-level ingestion')
const res: IngestAttachmentsResult = await ext.ingestAttachmentsForProject(projectId, [
{ path: attachment.path!, name: attachment.name, type: attachment.fileType, size: attachment.size },
])
const files = res.files
if (Array.isArray(files) && files[0]?.id) {
return {
id: files[0].id,
size: typeof files[0].size === 'number' ? Number(files[0].size) : undefined,
chunkCount: typeof files[0].chunk_count === 'number' ? Number(files[0].chunk_count) : undefined,
}
}
throw new Error('Failed to resolve ingested attachment id')View on GitHub (pinned to 7205d770c1)