{"record":{"id":"f8d3d00e34c46d7c","repo":"janhq/jan","slug":"file-file-name-has-already-been-attached-to-t","errorCode":null,"errorMessage":"File '${file.name}' has already been attached to this project","messagePattern":"File '(.+?)' has already been attached to this project","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"extensions/vector-db-extension/src/index.ts","lineNumber":107,"sourceCode":"    const text = await ragApi.parseDocument(file.path, file.type || 'application/octet-stream')\n    const chunks = await this.chunkText(text, opts.chunkSize, opts.chunkOverlap)\n\n    // Get embeddings to determine dimension - use a default if no chunks\n    let dimension = 0\n    if (chunks.length > 0) {\n      const embeddings = await embedTexts(chunks)\n      dimension = embeddings[0]?.length || 0\n    }\n\n    // Ensure collection exists (use default dimension 384 if no embeddings yet)\n    const collectionDimension = dimension > 0 ? dimension : 384\n    await this.createCollectionForProject(projectId, collectionDimension)\n\n    // Now check for duplicates\n    const existingFiles = await vecdb.listAttachments(this.collectionForProject(projectId)).catch(() => [])\n    const duplicate = existingFiles.find((f: any) => f.name === file.name && f.path === file.path)\n    if (duplicate) {\n      throw new Error(`File '${file.name}' has already been attached to this project`)\n    }\n\n    if (!chunks.length) {\n      const fi = await vecdb.createFile(this.collectionForProject(projectId), file)\n      return fi\n    }\n\n    // Re-embed if we got dimension from createCollection\n    const embeddings = await embedTexts(chunks)\n    const finalDimension = embeddings[0]?.length || 0\n    if (finalDimension <= 0) throw new Error('Embedding dimension not available')\n\n    // Ensure collection has correct dimension\n    if (finalDimension !== collectionDimension) {\n      await this.deleteCollectionForProject(projectId)\n      await this.createCollectionForProject(projectId, finalDimension)\n    }\n","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/janhq/jan/blob/7205d770c1e097c3daf35a911176410e93bc5564/extensions/vector-db-extension/src/index.ts#L89-L125","documentation":"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.","triggerScenarios":"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.","commonSituations":"Retry logic re-uploads the same attachment; user double-clicks an attach button; importing a project snapshot that already contains the file.","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"],"exampleFix":"// before\nawait vecdb.ingestFileForProject(projectId, file, chunks)\n// after\nconst existing = await vecdb.listAttachments(collectionName).catch(() => [])\nif (!existing.some(f => f.name === file.name && f.path === file.path)) {\n  await vecdb.ingestFileForProject(projectId, file, chunks)\n}","handlingStrategy":"try-catch","validationCode":"const existing = await vecdb.listAttachments(vecdb.collectionForProject(projectId)).catch(() => [])\nconst isDuplicate = existing.some(f => f.name === file.name && f.path === file.path)\nif (isDuplicate) return // skip already-attached file","typeGuard":null,"tryCatchPattern":"try {\n  await vecdb.ingestFileForProject(projectId, file, chunks)\n} catch (e) {\n  if (/has already been attached/.test(e.message)) {\n    // treat as idempotent success / skip\n  } else throw e\n}","preventionTips":["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"],"tags":["duplicate","idempotency","vector-db"],"backgroundTag":"file-already-exists","analyzedSha":"7205d770c1e097c3daf35a911176410e93bc5564","analyzedAt":"2026-09-17T14:27:30.100Z","contentChangedAt":"2026-09-17T14:27:30.100Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}