{"record":{"id":"e38c97cc98cfac45","repo":"mastra-ai/mastra","slug":"filesystemstorage-duplicate-file-path-file-pa","errorCode":null,"errorMessage":"[FilesystemStorage] duplicate file path: ${file.path}","messagePattern":"\\[FilesystemStorage\\] duplicate file path: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"mastracode/factory/src/storage/domains/filesystem/base.ts","lineNumber":67,"sourceCode":"    !value ||\n    value.startsWith('/') ||\n    value.split('/').some(segment => !segment || segment === '.' || segment === '..')\n  ) {\n    throw new Error('[FilesystemStorage] path must be a non-empty relative path.');\n  }\n}\n\nfunction assertScope(args: { resourceId: string; threadId: string }): void {\n  assertIdentifier(args.resourceId, 'resourceId');\n  assertIdentifier(args.threadId, 'threadId');\n}\n\nfunction validateFiles(files: FilesystemFile[]): void {\n  const paths = new Set<string>();\n\n  for (const file of files) {\n    assertRelativePath(file.path);\n    if (paths.has(file.path)) throw new Error(`[FilesystemStorage] duplicate file path: ${file.path}`);\n    paths.add(file.path);\n  }\n}\n\nexport class FilesystemStorage extends FactoryStorageDomain {\n  constructor() {\n    super('filesystem');\n  }\n\n  async init(): Promise<void> {\n    await this.ensureCollections(FILESYSTEM_SCHEMAS);\n  }\n\n  async dangerouslyClearAll(): Promise<void> {\n    await this.ops.deleteMany(SNAPSHOTS, {});\n  }\n\n  async replaceFiles(input: ReplaceFilesystemFilesInput): Promise<void> {","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/mastracode/factory/src/storage/domains/filesystem/base.ts#L49-L85","documentation":"validateFiles builds a Set of paths while iterating the supplied files; encountering the same path twice throws '[FilesystemStorage] duplicate file path: <path>'. Storage is keyed by path, so a batch containing duplicates is ambiguous and rejected wholesale.","triggerScenarios":"Calling replaceFiles (which calls validateFiles) with a files array containing two FilesystemFile entries sharing the same path value.","commonSituations":"Merging uploads with existing state where the same file appears twice; deduplication lost after a refactor; batch built from multiple sources that can both include e.g. 'README.md'.","solutions":["Deduplicate the array by path (keep the latest version) before calling replaceFiles","Fix the merge logic so each path appears once per batch","If intent is to append versions, use the appropriate upsert/single-file API instead of a duplicate batch"],"exampleFix":"// before\nawait storage.replaceFiles({ resourceId, threadId, files: [...existing, ...incoming] });\n// after\nconst byPath = new Map([...existing, ...incoming].map(f => [f.path, f]));\nawait storage.replaceFiles({ resourceId, threadId, files: [...byPath.values()] });","handlingStrategy":"validation","validationCode":"const deduped = [...new Map(files.map(f => [f.path, f])).values()];\nif (deduped.length !== files.length) throw new Error('Duplicate paths in file batch');","typeGuard":null,"tryCatchPattern":"try {\n  await storage.replaceFiles({ resourceId, threadId, files });\n} catch (e) {\n  if (e instanceof Error && e.message.includes('duplicate file path')) {\n    logger.warn('Deduplicating file batch and retrying once');\n    await storage.replaceFiles({ resourceId, threadId, files: dedupeByPath(files) });\n  } else throw e;\n}","preventionTips":["Dedupe by path whenever merging file batches from multiple sources","Keep a single source of truth for file state instead of concatenating lists","Add a lint/test asserting batch invariants before storage writes","Prefer upsert APIs for repeated writes to the same path"],"tags":["validation","duplicate","filesystem","batch"],"backgroundTag":"duplicate-key","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}