janhq/jan · error · Error

ingestFileAttachment: attachment is not document

Error message

ingestFileAttachment: attachment is not document

What it means

Thrown by DefaultUploadsService.ingestFileAttachment when attachment.type !== 'document'. It is a contract guard - this method only handles document attachments; passing anything else is a caller bug.

Source

Thrown at web-app/src/services/uploads/default.ts:16

import type { UploadsService, UploadResult } from './types'
import type { Attachment } from '@/types/attachment'
import { ulid } from 'ulidx'
import { ExtensionManager } from '@/lib/extension'
import { ExtensionTypeEnum, type RAGExtension, type IngestAttachmentsResult } from '@janhq/core'

export class DefaultUploadsService implements UploadsService {
  async ingestImage(_threadId: string, attachment: Attachment): Promise<UploadResult> {
    if (attachment.type !== 'image') throw new Error('ingestImage: attachment is not image')
    // Placeholder upload flow; swap for real API call when backend is ready
    await new Promise((r) => setTimeout(r, 100))
    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')

View on GitHub (pinned to fad3f12a14)

Solutions

  1. Route only document attachments to ingestFileAttachment.
  2. Dispatch by attachment.type from a single location.

Example fix

// before: document path receives all attachments
await uploads.ingestFileAttachment(threadId, att)
// after: dispatch by type
if (att.type === 'document') await uploads.ingestFileAttachment(threadId, att)
else if (att.type === 'image') await uploads.ingestImage(threadId, att)
Defensive patterns

Strategy: validation

Validate before calling

if (attachment.type !== 'document') throw new Error('Skip non-document')

Type guard

function isDocumentAttachment(a: Attachment): boolean { return a.type === 'document' }

Prevention

When it happens

Trigger: ingestFileAttachment(threadId, attachment) is called with an attachment whose type field is not 'document' (e.g., an image or unknown type routed into the document path).

Common situations: Incorrect dispatch in the upload pipeline; an image attachment reaching the document path; attachment type values changed during a refactor.

Related errors


AI-assisted analysis of janhq/jan@fad3f12a14 (2026-08-12). Data as JSON: /api/errors/257ff583f7dac1b1. Report an issue: GitHub.