{"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":"validation","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 this.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 this.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/fad3f12a147d138388a66f0d92a02b2675f65294/extensions/vector-db-extension/src/index.ts#L89-L125","documentation":"Thrown by VectorDBExtension.ingestFileForProject() after the collection is created and existing attachments are listed, when an attachment with the same name AND path already exists. Duplicate detection is by (name, path) pair, so a renamed copy or a same-name file from a different path is allowed. Note the collection is created before the duplicate check, so a rejected ingest still leaves the collection in place.","triggerScenarios":"Calling ingestFileForProject twice with the same file; re-running ingestion after a partial failure that already created the attachment record; a retry loop that does not dedupe.","commonSituations":"User re-attaches the same document; automation re-ingests a project on every run; a resume-after-crash retries the already-recorded file.","solutions":["Check existing attachments via listAttachmentsForProject before ingesting and skip duplicates.","Delete the existing attachment (deleteFileForProject) before re-ingesting if you want a fresh copy.","Dedupe the file list by (name, path) before calling ingest.","Treat this error as a no-op success if idempotent re-ingest is desired."],"exampleFix":"// before\nawait vecdbExt.ingestFileForProject(projectId, file, opts)\n\n// after\nconst existing = await vecdbExt.listAttachmentsForProject(projectId)\nconst dup = existing.find((f) => f.name === file.name && f.path === file.path)\nif (dup) return dup // idempotent: return the existing record\nawait vecdbExt.ingestFileForProject(projectId, file, opts)","handlingStrategy":"validation","validationCode":"const existing = await vecdbExt.listAttachmentsForProject(projectId)\nconst isDup = existing.some((f) => f.name === file.name && f.path === file.path)\nif (isDup) {\n  // skip, or deleteFileForProject first to refresh\n}","typeGuard":null,"tryCatchPattern":"try {\n  await vecdbExt.ingestFileForProject(projectId, file, opts)\n} catch (e) {\n  if (e instanceof Error && e.message.includes('already been attached')) return // idempotent\n  throw e\n}","preventionTips":["Dedupe the upload list by (name, path) before ingesting.","Track attached files in UI state to prevent re-attach.","Delete the old record before re-ingesting if a refresh is intended."],"tags":["vector-db","duplicate","project","ingestion"],"backgroundTag":null,"analyzedSha":"fad3f12a147d138388a66f0d92a02b2675f65294","analyzedAt":"2026-08-12T20:33:47.516Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}