{"record":{"id":"055eb15efbacd6dc","repo":"agalwood/Motrix","slug":"taskcreatededupexhausted","errorCode":"TaskCreateDedupExhausted","errorMessage":"Too many files with name \"${desiredName}\" already exist in ${saveDir}","messagePattern":"Too many files with name \"(.+?)\" already exist in (.+?)","errorType":"exception","errorClass":"AppError","httpStatus":null,"severity":"error","filePath":"src/core/task/final-name-picker.ts","lineNumber":33,"sourceCode":"\nexport class FinalNamePickerImpl implements FinalNamePicker {\n  constructor(private readonly fs: FsProbe) {}\n\n  async pick(saveDir: string, desiredName: string): Promise<string> {\n    if (!(await this.isTaken(saveDir, desiredName))) {\n      return desiredName\n    }\n\n    const { base, ext } = splitNameExt(desiredName)\n\n    for (let n = 1; n <= MAX_DEDUP_ATTEMPTS; n++) {\n      const candidate = ext ? `${base} (${n})${ext}` : `${base} (${n})`\n      if (!(await this.isTaken(saveDir, candidate))) {\n        return candidate\n      }\n    }\n\n    throw new AppError(\n      ErrorCode.TaskCreateDedupExhausted,\n      `Too many files with name \"${desiredName}\" already exist in ${saveDir}`\n    )\n  }\n\n  private async isTaken(dir: string, name: string): Promise<boolean> {\n    const finalPath = path.join(dir, name)\n    const tempPath = finalPath + INCOMPLETE_SUFFIX\n    const [f, t] = await Promise.all([\n      this.fs.exists(finalPath),\n      this.fs.exists(tempPath),\n    ])\n    return f || t\n  }\n}\n\n/**\n * Split \"foo.mp4\" into { base: \"foo\", ext: \".mp4\" }.","sourceCodeStart":15,"sourceCodeEnd":51,"githubUrl":"https://github.com/agalwood/Motrix/blob/1a708ee57746c434e2c67a44bbf0906a976afea4/src/core/task/final-name-picker.ts#L15-L51","documentation":"Thrown by final-name-picker.ts:33 when picking a de-duplicated final name: it tried `desiredName`, then `desiredName (1)`, `(2)`, ... up to `MAX_DEDUP_ATTEMPTS` and every candidate already exists (as either the final path or its `.motrix` temp sibling). No unique slot could be found.","triggerScenarios":"The save directory already contains MAX_DEDUP_ATTEMPTS+1 files sharing the same base name (e.g. 50+ 'report.pdf' / 'report (1).pdf' ...); repeated re-downloads of the same filename into a busy directory; an external process is racing to create the same candidates.","commonSituations":"Bulk re-downloading the same attachment many times; a shared save dir used by many concurrent tasks with templated names; old completed downloads never cleaned; a sync/mirror folder already populated with numbered copies.","solutions":["Clean up or move aside the existing files in `saveDir` so a candidate slot is free.","Download to a fresh subdirectory per task rather than a shared flat dir.","Raise MAX_DEDUP_ATTEMPTS if a high-collision dir is intentional and supported.","Have the caller supply an explicit unique `filename` so dedup is bypassed."],"exampleFix":"// before\nfor (let n = 1; n <= MAX_DEDUP_ATTEMPTS; n++) {\n  const candidate = ext ? `${base} (${n})${ext}` : `${base} (${n})`\n  if (!(await this.isTaken(saveDir, candidate))) return candidate\n}\nthrow new AppError(ErrorCode.TaskCreateDedupExhausted, `Too many files with name \"${desiredName}\" already exist in ${saveDir}`)\n\n// after — fall back to a timestamped unique name instead of hard-failing\nconst candidate = ext ? `${base}-${Date.now()}${ext}` : `${base}-${Date.now()}`\nif (!(await this.isTaken(saveDir, candidate))) return candidate\nthrow new AppError(ErrorCode.TaskCreateDedupExhausted, `Too many files with name \"${desiredName}\" already exist in ${saveDir}`)","handlingStrategy":"validation","validationCode":"async function freeSlotExists(fs, saveDir, base, ext, max) {\n  if (!(await isTaken(saveDir, ext ? `${base}${ext}` : base))) return true\n  for (let n = 1; n <= max; n++) {\n    const c = ext ? `${base} (${n})${ext}` : `${base} (${n})`\n    if (!(await isTaken(saveDir, c))) return true\n  }\n  return false\n}\nif (!(await freeSlotExists(fs, saveDir, base, ext, MAX_DEDUP_ATTEMPTS))) {\n  // ask user for an explicit filename, or pick a new subdir\n}","typeGuard":"function isDedupExhausted(e) { return e instanceof AppError && e.code === ErrorCode.TaskCreateDedupExhausted }","tryCatchPattern":"try {\n  finalName = await finalNamePicker.pick(saveDir, desiredName)\n} catch (e) {\n  if (e instanceof AppError && e.code === ErrorCode.TaskCreateDedupExhausted) {\n    finalName = await finalNamePicker.pick(saveDir, `${desiredName}-${Date.now()}`)\n  } else throw e\n}","preventionTips":["Use a per-task subdirectory rather than a flat shared dir for repeated names.","Have the caller supply an explicit unique filename to bypass dedup.","Clean up old completed files that share the base name.","Raise MAX_DEDUP_ATTEMPTS if high-collision dirs are intentional."],"tags":["create","filesystem","naming","dedup","io"],"backgroundTag":null,"analyzedSha":"1a708ee57746c434e2c67a44bbf0906a976afea4","analyzedAt":"2026-08-12T16:18:09.346Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}