janhq/jan · error
ingestImage: attachment is not image
Error message
ingestImage: attachment is not image
What it means
DefaultUploadsService.ingestImage is a single-purpose method that only accepts attachments whose type is exactly 'image'. It throws this error immediately when handed an Attachment with any other type (e.g. 'document'). The check is a guard in front of a placeholder upload flow that returns a generated ulid.
Solutions
- Check attachment.type === 'image' before calling ingestImage and route non-image attachments to ingestFileAttachment instead
- Fix the code that constructs the Attachment so image files get type 'image'
- If the call site legitimately receives mixed attachments, branch on type rather than calling ingestImage unconditionally
Example fix
// before const result = await uploads.ingestImage(threadId, attachment) // after const result = attachment.type === 'image' ? await uploads.ingestImage(threadId, attachment) : await uploads.ingestFileAttachment(threadId, attachment)
Defensive patterns
Strategy: type-guard
Validate before calling
if (attachment.type !== 'image') { console.warn('Skipping non-image attachment', attachment.name); return } Type guard
const isImageAttachment = (a: Attachment): a is Attachment & { type: 'image' } => a.type === 'image' Try / catch
try {
const result = await uploads.ingestImage(threadId, att)
} catch (e) {
if (e instanceof Error && e.message.includes('attachment is not image')) {
return uploads.ingestFileAttachment(threadId, att)
}
throw e
} Prevention
- Always dispatch on attachment.type before choosing an ingest method
- Add a discriminated-union type for Attachment so TypeScript forces narrowing on type
- Validate attachment.type at the file-picker boundary when the Attachment is first constructed
When it happens
Trigger: Calling uploadsService.ingestImage(threadId, attachment) with an attachment whose attachment.type is not 'image' — e.g. passing a document/PDF attachment or an attachment whose type was never set during file selection.
Common situations: A caller routes all attachments to ingestImage without inspecting type first; a file picker produces attachments with type inferred incorrectly; a refactor changed the Attachment type union and the caller's mapping was not updated.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- ingestFileAttachment: attachment is not document
- ingestFileAttachmentForProject: attachment is not document
AI-assisted analysis of janhq/jan@7205d770c1 (2026-09-17).
Data as JSON: /api/errors/8cfd9d16c7174e74.
Report an issue: GitHub.
Appendix: source
Thrown at web-app/src/services/uploads/default.ts:9
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,View on GitHub (pinned to 7205d770c1)