Save-VaultFile fails with "Collection was modified; enumeration operation may not execute."

When calling Save-VaultFile the following misleading message is logged to the logfile: enumeration operation may not execute.

Issue

When calling Save-VaultFile the following misleading message is logged to the logfile: 

System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
at System.Collections.Generic.List`1.Enumerator.MoveNextRare()
at System.Linq.Enumerable.WhereListIterator`1.MoveNext()
at powerVault.Cmdlets.Cmdlets.Vault.Facade.Files.ListExtensions.RemoveAndGet[T](IList`1 list, Func`2 predicate)

Cause

The error message is misleading. It is displayed when a file couldn't be downloaded for one of the following reasons.

  • The Vault user that executes Save-VaultFile has no download permissions on that file or one of its children
  • The local windows path is to long for the downloaded file or one of its children

Solution

Permissions

First you should verify if this is related to the download permissions and if so, which files are affected

  • Login to Vault Client with the same user that is logged in to the Jobprocessor.
  • Search for the affected file
  • Right click on the drawing and select "Get..."
  • In the Get dialog select "Include Children"
  • Press Ok

If you don't have permissions to download any of the dependencies an error dialog will appear that lists all those files. Take a look at those file's lifecycle and make sure to grant the job user download permissions for that particular lifecycle and state. Please refer to the Vault documentation for information how to change the lifecycle security.

NTFS Path Length

To check whether one of your files exceeds the ntfs path limit you can use the PowerShell script below.

  • Login to Vault Client with the same user that is logged in to the Jobprocessor.
  • Search for the affected drawing
  • Right click on the drawing and select "Get..."
  • In the Get dialog select "Include Children"
  • Press Ok
  • In Windows search for ISE and run Windows PowerShell ISE
  • In ISE in the white pane insert the below script
  • Change the line "$vaultWorkspace = 'C:\VaultWorkspaces\Vault'" to your actual local VAult workspace. For example "$vaultWorkspace = 'C:\Vault'"
  • Save the script
  • Press F5 to run the script
class FileCount {
    [string] $Path
    [int] $Length

    FileCount([string]$path) {
        $this.Path = $path
        $this.Length = $path.Length
    }
}

$vaultWorkspace = 'C:\VaultWorkspaces\Vault'

$fileCounts = @()
$filePaths = Get-ChildItem -LiteralPath $VaultWorkspace -Recurse -File | Select-Object -ExpandProperty FullName
foreach ($filePath in $filePaths) {
    $fileCounts += [FileCount]::new($filePath)
}
$largestPath = $fileCounts | Sort-Object -Property Length -Descending | Select-Object -ExpandProperty Path -First 1

$powerJobsRoot = 'C:\TEMP\powerJobs Processor\00000000-0000-0000-0000-000000000000'

$filePathWithoutWorkspace = $largestPath.Replace($vaultWorkspace, '')
$largestTempPath = Join-Path -Path $powerJobsRoot -ChildPath $filePathWithoutWorkspace

Write-Host ""

$fileCounts | Sort-Object Length `
| ForEach-Object { Write-Host ("{0} - {1}" -f $_.Length, $_.Path) } 

Write-Host ""
Write-Host "The largest file path in the temporary powerJobs file will be $($largestTempPath.Length): '$($largestTempPath)'"
Write-Host ""