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

  1. Log/res.inspect the raw IngestAttachmentsResult from the RAG extension to see what was actually returned
  2. Verify the backend ingestion endpoint actually persists the file and returns its id
  3. 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

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


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)