janhq/jan · error · Error
ingestImage: attachment is not image
Error message
ingestImage: attachment is not image
What it means
Thrown by DefaultUploadsService.ingestImage when attachment.type !== 'image'. It is a contract guard - this method only handles image attachments; routing anything else here is a caller bug, not a runtime/environment condition.
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 fad3f12a14)
Solutions
- Ensure the caller routes only image attachments to ingestImage.
- Dispatch by attachment.type from a single location.
Example fix
// before: every attachment routed to ingestImage await uploads.ingestImage(threadId, att) // after: dispatch by type if (att.type === 'image') await uploads.ingestImage(threadId, att) else if (att.type === 'document') await uploads.ingestFileAttachment(threadId, att)
Defensive patterns
Strategy: validation
Validate before calling
if (attachment.type !== 'image') throw new Error('Skip non-image') Type guard
function isImageAttachment(a: Attachment): boolean { return a.type === 'image' } Prevention
- Centralize type-based dispatch in one place.
- Use a discriminated union for Attachment to get compile-time safety on type.
When it happens
Trigger: ingestImage(threadId, attachment) is called with an attachment whose type field is not 'image' (e.g., a document, PDF, or unknown type routed into the image path).
Common situations: Incorrect dispatch in the upload pipeline; an attachment misclassified before reaching the service; attachment type values changed during a refactor.
Related errors
- ingestFileAttachment: attachment is not document
- ingestFileAttachmentForProject: attachment is not document
- Invalid backend string: ${targetBackendString} supplied to u
- Invalid backend string format: "${targetBackendString}". Exp
- Invalid path or file ${path}
AI-assisted analysis of janhq/jan@fad3f12a14 (2026-08-12).
Data as JSON: /api/errors/8cfd9d16c7174e74.
Report an issue: GitHub.