janhq/jan · error · Error
No ID returned from image ingestion
Error message
No ID returned from image ingestion
What it means
This error is thrown in the React frontend when serviceHub.uploads().ingestImage() resolves successfully but the returned object has no id property (or id is falsy). The image ingestion pipeline is expected to return an object with an id field representing the stored vector/embedding. A missing id means the backend accepted the request but did not return a valid identifier, making the attachment unusable for retrieval.
Source
Thrown at web-app/src/containers/ChatInput.tsx:1091
const result = await serviceHub
.uploads()
.ingestImage(currentThreadId, img)
if (result?.id) {
setAttachmentsForThread(attachmentsKey, (prev) =>
prev.map((a) =>
matchImg(a)
? {
...a,
processing: false,
processed: true,
id: result.id,
}
: a
)
)
} else {
throw new Error('No ID returned from image ingestion')
}
} catch (error) {
console.error('Failed to ingest image:', error)
setAttachmentsForThread(attachmentsKey, (prev) =>
prev.filter((a) => !matchImg(a))
)
toast.error(`Failed to ingest ${img.name}`, {
description:
error instanceof Error ? error.message : String(error),
})
} finally {
setFileIngestProgress({
completed: i + 1,
total: ingestTotal,
})
}
}
} finally {View on GitHub (pinned to fad3f12a14)
Solutions
- Verify the vector-db plugin is initialized and healthy before ingesting images.
- Check the backend ingestImage command's return type matches { id: string }.
- Log the full result object to inspect the actual response shape.
- Ensure the SQLite/vector store has the correct schema for image embeddings.
Example fix
// before
const result = await serviceHub.uploads().ingestImage(currentThreadId, img)
if (result?.id) { /* use result.id */ }
else { throw new Error('No ID returned from image ingestion') }
// after
const result = await serviceHub.uploads().ingestImage(currentThreadId, img)
const id = result?.id ?? result?.uuid ?? result?._id
if (!id) {
console.error('Unexpected ingest result shape:', JSON.stringify(result))
throw new Error(`No ID returned from image ingestion: ${JSON.stringify(result)}`)
} Defensive patterns
Strategy: type-guard
Validate before calling
// Before using the result, validate its shape
interface IngestResult { id: string }
function isIngestResult(v: unknown): v is IngestResult {
return typeof v === 'object' && v !== null &&
typeof (v as Record<string, unknown>).id === 'string' &&
(v as Record<string, unknown>).id!.length > 0;
}
const result = await serviceHub.uploads().ingestImage(currentThreadId, img);
if (!isIngestResult(result)) {
console.error('Unexpected ingest result:', JSON.stringify(result));
throw new Error(`No ID returned from image ingestion: ${JSON.stringify(result)}`);
} Type guard
function hasValidId(v: unknown): v is { id: string } {
return typeof v === 'object' && v !== null &&
typeof (v as Record<string, unknown>).id === 'string' &&
((v as Record<string, unknown>).id as string).length > 0;
} Try / catch
try {
const result = await serviceHub.uploads().ingestImage(currentThreadId, img);
if (!result?.id) {
throw new Error(`No ID returned: ${JSON.stringify(result)}`);
}
// use result.id
} catch (error) {
setAttachmentsForThread(attachmentsKey, (prev) => prev.filter((a) => !matchImg(a)));
toast.error(`Failed to ingest ${img.name}`, {
description: error instanceof Error ? error.message : String(error),
});
} Prevention
- Type-guard the ingest result before accessing .id.
- Log the full result object when the id is missing to diagnose backend issues.
- Verify the vector-db plugin is initialized before allowing image uploads.
- Coordinate the response contract between frontend and backend in a shared type.
When it happens
Trigger: The vector-db plugin's ingestImage command returns an empty or null result object. The backend stored the image but failed to return the generated ID (serialization issue). A race condition where the result is partially constructed. The backend returned a different response shape than expected (e.g. { uuid } instead of { id }).
Common situations: Vector database not initialized when ingestion is attempted. Plugin version mismatch changing the response contract. Backend returning { ok: true } without an id. SQLite insert succeeded but last_insert_rowid() returned 0. Network/IPC serialization dropping the id field.
Related errors
- settings:general.couldNotRelocateToRoot
- No model available to load
- Checksum mismatch for ${name}; the download was corrupt or t
- Import of "${FALLBACK_EMBEDDING_MODEL_ID}" did not complete
- No supported backend binaries found for this system. Backend
AI-assisted analysis of janhq/jan@fad3f12a14 (2026-08-12).
Data as JSON: /api/errors/5ebce1326fddc237.
Report an issue: GitHub.