powershellparameter-passingjobslimits

Max size of ScriptBlock / InitializationScript for Start-Job in PowerShell


When you start a new job with Start-Job, you can pass it a ScriptBlock and a InitializationScript for example:

Function FOO {
  Write-Host "HEY"
} 
Start-Job -ScriptBlock {FOO} -InitializationScript {
  Function Foo { $function:FOO }
} | Wait-Job | Receive-Job

There seems to be a limit to the size of the initialization script you can pass, if it is too big then you get an error such as

[localhost] An error occurred while starting the background process. Error
reported: The filename or extension is too long.
    + CategoryInfo          : OpenError: (localhost:String) [], PSRemotingTransportException
    + FullyQualifiedErrorId : -2147467259,PSSessionStateBroken

Behind the scenes, PowerShell is creating a new process and passing InitializationScript as a Base64 encoded command line parameter.

According to the Win32 CreateProcess() function, the max size of the command ine is 32,768 characters. So obviously if your Base64 encoded InitializationScript is getting near this size then you will probably get an error.

I haven't yet found a limit for the size of the ScriptBlock parameter. Can someone confirm that there is no limit?

I assume that there is no limit because it looks like the ScriptBlock is transmitted to the child process via standard input?


Solution

  • Your guess was correct.

    PowerShell translates a Start-Job call into a PowerShell CLI call behind the scenes (to powershell.exe for Windows PowerShell, and to pwsh for PowerShell (Core) 7+), that is, it achieves parallelism via a child process that the calling PowerShell session communicates with, using the standard in- and output streams:

    For instance, the following Start-Job call:

    Start-Job -ScriptBlock { [Environment]::CommandLine } -InitializationScript { 'hi' > $null } | 
      Receive-Job -Wait -AutoRemoveJob
    

    reveals that the background-job child process was launched as follows, when run from Windows PowerShell:

    "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -Version 5.1 -s -NoLogo -NoProfile -EncodedCommand IAAnAGgAaQAnACAAPgAgACQAbgB1AGwAbAAgAA==
    

    As you can see, the -ScriptBlock argument's text is not present in the resulting command line (it was sent via stdin), whereas the -InitializationScript argument's is, as the Base64-encoded string passed to -EncodedCommand, which you can verify as follows:

    # -> " 'hi' > $null ", i.e. the -InitializationScript argument, sans { and }
    [Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('IAAnAGgAaQAnACAAPgAgACQAbgB1AGwAbAAgAA=='))`)
    

    As for the other parameters:


    [1] [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes('x' * 12244)).Length yields 32652, which is the closest you can come to the 32655 limit; an input length of 12245 yields 32656.