janhq/jan · error · Error
File ' ' has already been attached to this project
Error message
File '${file.name}' has already been attached to this project What it means
ingestFileForProject checks the project's vector collection for an existing attachment with the same name AND path before ingesting. A duplicate match means the file was already attached to this project, so re-ingestion is refused to avoid duplicate chunks.
Solutions
- Check existing attachments (listAttachments on the project collection) and skip files already present before calling ingestFileForProject
- Catch this error and treat it as a no-op/success for idempotent ingestion
- Use a unique path or updated file name if the file content genuinely differs
- Delete the existing attachment first if re-ingestion is intended
Example fix
// before
await vecdb.ingestFileForProject(projectId, file, chunks)
// after
const existing = await vecdb.listAttachments(collectionName).catch(() => [])
if (!existing.some(f => f.name === file.name && f.path === file.path)) {
await vecdb.ingestFileForProject(projectId, file, chunks)
} Defensive patterns
Strategy: try-catch
Validate before calling
const existing = await vecdb.listAttachments(vecdb.collectionForProject(projectId)).catch(() => []) const isDuplicate = existing.some(f => f.name === file.name && f.path === file.path) if (isDuplicate) return // skip already-attached file
Try / catch
try {
await vecdb.ingestFileForProject(projectId, file, chunks)
} catch (e) {
if (/has already been attached/.test(e.message)) {
// treat as idempotent success / skip
} else throw e
} Prevention
- Debounce/duplicate-check attach actions in the UI (disable button after click)
- Make ingestion pipelines idempotent by checking listAttachments first
- Distinguish retry-after-failure from genuine new uploads
When it happens
Trigger: Calling ingestFileForProject twice for the same file (same name and path) on the same projectId, or re-running an ingestion pipeline that retries after a partial failure.
Common situations: Retry logic re-uploads the same attachment; user double-clicks an attach button; importing a project snapshot that already contains the file.
Understand the failure class
Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.
Related errors
- Database error
- Embedding dimension not available
- File ' ' has already been attached to this thread
- Invalid input
- llamacpp extension not available
AI-assisted analysis of janhq/jan@7205d770c1 (2026-09-17).
Data as JSON: /api/errors/f8d3d00e34c46d7c.
Report an issue: GitHub.
Appendix: source
Thrown at extensions/vector-db-extension/src/index.ts:107
const text = await ragApi.parseDocument(file.path, file.type || 'application/octet-stream')
const chunks = await this.chunkText(text, opts.chunkSize, opts.chunkOverlap)
// Get embeddings to determine dimension - use a default if no chunks
let dimension = 0
if (chunks.length > 0) {
const embeddings = await embedTexts(chunks)
dimension = embeddings[0]?.length || 0
}
// Ensure collection exists (use default dimension 384 if no embeddings yet)
const collectionDimension = dimension > 0 ? dimension : 384
await this.createCollectionForProject(projectId, collectionDimension)
// Now check for duplicates
const existingFiles = await vecdb.listAttachments(this.collectionForProject(projectId)).catch(() => [])
const duplicate = existingFiles.find((f: any) => f.name === file.name && f.path === file.path)
if (duplicate) {
throw new Error(`File '${file.name}' has already been attached to this project`)
}
if (!chunks.length) {
const fi = await vecdb.createFile(this.collectionForProject(projectId), file)
return fi
}
// Re-embed if we got dimension from createCollection
const embeddings = await embedTexts(chunks)
const finalDimension = embeddings[0]?.length || 0
if (finalDimension <= 0) throw new Error('Embedding dimension not available')
// Ensure collection has correct dimension
if (finalDimension !== collectionDimension) {
await this.deleteCollectionForProject(projectId)
await this.createCollectionForProject(projectId, finalDimension)
}
View on GitHub (pinned to 7205d770c1)