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

  1. Check existing attachments (listAttachments on the project collection) and skip files already present before calling ingestFileForProject
  2. Catch this error and treat it as a no-op/success for idempotent ingestion
  3. Use a unique path or updated file name if the file content genuinely differs
  4. 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

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


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)