agalwood/Motrix · warning · FsTaskError

plugin.fs.rename_target_exists

plugin.fs.rename_target_exists

Error message

plugin.fs.rename_target_exists: ${newFilename} already exists in saveDir

What it means

Thrown by FsTask.rename(newFilename) when `fs.access(newPath)` succeeds — i.e. a file with the proposed name already exists in saveDir. The rename is intentionally non-clobbering to prevent accidental overwrites; the existence check runs before `fs.rename`. Code is `plugin.fs.rename_target_exists`. Note assertBasename() runs first, so an invalid name throws invalid_basename instead.

Source

Thrown at src/core/plugin/capabilities/fs-task.ts:268

      stream.on('end', () => resolve(hash.digest('hex')))
    })
  }

  // -------------------------------------------------------------------------
  // rename
  // -------------------------------------------------------------------------

  async rename(newFilename: string): Promise<void> {
    // Throws FsSandboxError with code 'plugin.fs.invalid_basename' if not valid
    assertBasename(newFilename)

    const newPath = path.join(this.saveDir, newFilename)

    // Check target doesn't already exist
    try {
      await fs.access(newPath)
      // If we get here, file exists
      throw new FsTaskError(
        'plugin.fs.rename_target_exists',
        `plugin.fs.rename_target_exists: ${newFilename} already exists in saveDir`
      )
    } catch (e: unknown) {
      if (e instanceof FsTaskError) throw e
      const err = e as NodeJS.ErrnoException
      if (err.code !== 'ENOENT') throw e
      // ENOENT = target doesn't exist, proceed
    }

    await fs.rename(this._filePath, newPath)
    this._filePath = newPath
  }

  // -------------------------------------------------------------------------
  // disposeAllReaders
  // -------------------------------------------------------------------------

View on GitHub (pinned to 1a708ee577)

Solutions

  1. Generate a unique name (append a UUID, hash, or high-resolution timestamp) before renaming.
  2. Check existence yourself and either delete-then-rename or pick an alternate name.
  3. Clean the output directory at the start of the run if clobbering is acceptable.
  4. Use a per-run subdirectory under saveDir to isolate outputs.

Example fix

// before
await task.rename('result.json') // throws if a prior run left one

// after — unique name per run
await task.rename(`result-${crypto.randomUUID()}.json`)
Defensive patterns

Strategy: validation

Validate before calling

async function uniqueName(saveDir: string, base: string): Promise<string> {
  const ext = path.extname(base)
  const stem = path.basename(base, ext)
  for (let i = 0; ; i++) {
    const candidate = `${stem}-${i}${ext}`
    try { await fs.access(path.join(saveDir, candidate)); continue }
    catch { return candidate }
  }
}

Type guard

function isRenameTargetExists(e: unknown): boolean {
  return e instanceof Error && (e as FsTaskError).code === 'plugin.fs.rename_target_exists'
}

Try / catch

try {
  await task.rename(newFilename)
} catch (e) {
  if (isRenameTargetExists(e)) { /* pick a unique name and retry */ }
  else throw e
}

Prevention

When it happens

Trigger: Calling `task.rename('out.txt')` when `saveDir/out.txt` already exists; rename collides with a previous run's output; concurrent tasks writing to the same directory with predictable names.

Common situations: Re-running a pipeline without cleaning the output dir; timestamp-based names that collide on rapid reruns; two workers picking the same derived name.

Related errors


AI-assisted analysis of agalwood/Motrix@1a708ee577 (2026-08-12). Data as JSON: /api/errors/700309328e188062. Report an issue: GitHub.