Skip to content

Make SetupWellKnownPaths resilient so a missing profile.ps1 cannot terminate the PowerShell 7.6 (.NET 10) worker #1146

Description

@andystaples

Summary

FunctionLoader.SetupWellKnownPaths enumerates the function app root directory with a filtered Directory.EnumerateFiles(..., "profile.ps1", ...) call and no existence check or fallback. On the PowerShell 7.6 worker (.NET 10), this enumeration can throw a hard, terminating failure of the worker for apps that do not have a profile.ps1, even though the content share (C:\home\site\wwwroot) is mounted and readable. This issue tracks making the worker resilient to that failure; #1147 covers the underlying .NET runtime root cause.

Observed failure

Microsoft.Azure.WebJobs.Script.Workers.Rpc.RpcException : Result: Failure
Exception: Could not find file 'C:\home\site\wwwroot'.
Stack:    at System.IO.Enumeration.FileSystemEnumerator`1.FindNextEntry()
   at System.IO.Enumeration.FileSystemEnumerator`1.MoveNext()
   at System.Linq.Enumerable.TryGetFirstNonIterator[TSource](IEnumerable`1 source, Boolean& found)
   at System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 source)
   at Microsoft.Azure.Functions.PowerShellWorker.FunctionLoader.SetupWellKnownPaths(FunctionLoadRequest request, String managedDependenciesPath)
   at Microsoft.Azure.Functions.PowerShellWorker.RequestProcessor.SetupAppRootPathAndModulePath(FunctionLoadRequest functionLoadRequest, String managedDependenciesPath)
   at Microsoft.Azure.Functions.PowerShellWorker.RequestProcessor.ProcessFunctionLoadRequest(StreamingMessage request)

Relevant code

src/FunctionLoader.cs:

// Resolve the FunctionApp profile path
var options = new EnumerationOptions { MatchCasing = MatchCasing.CaseInsensitive };
var profiles = Directory.EnumerateFiles(FunctionAppRootPath, "profile.ps1", options);
FunctionAppProfilePath = profiles.FirstOrDefault();

Directory.EnumerateFiles is lazy, so the directory is walked when FirstOrDefault() pulls the first entry — which is exactly where the stack trace lands. The resolved path in the error is the correct, fully-qualified C:\home\site\wwwroot, so this is not a path-computation bug — the worker computed the right directory and then failed while enumerating it.

Impact

ProcessFunctionLoadRequest caches the exception into _initTerminatingError, so a single failure here is treated as terminating: every subsequent FunctionLoadRequest returns the same failure until the worker is recycled.

Proposed enhancement

Make SetupWellKnownPaths resilient so a profile.ps1 lookup can never take down the worker:

  1. Resolve profile.ps1 without a filtered enumeration. Replace the filtered Directory.EnumerateFiles(FunctionAppRootPath, "profile.ps1", ...) + FirstOrDefault() with a direct existence check on the known filename, e.g.:

    var profilePath = Path.Combine(FunctionAppRootPath, "profile.ps1");
    FunctionAppProfilePath = File.Exists(profilePath) ? profilePath : null;

    The filename is fixed and known, so there is no need to enumerate the directory with an OS-level filter at all. File.Exists does not go through the filtered FileSystemEnumerator code path and returns false (rather than throwing) when the file is absent — which is exactly the "no profile.ps1" case that triggers the failure on the affected .NET versions (see SetupWellKnownPaths wwwroot enumeration fails only on PowerShell 7.6 (.NET 10) worker — Directory.EnumerateFiles regression in .NET 10.0.9, fixed in 10.0.11 #1147). On Windows the file system is case-insensitive, so this preserves the existing case-insensitive lookup behavior; if strict cross-platform case-insensitive matching is required, guard a single-directory enumeration behind a Directory.Exists check and a try/catch.

  2. Do not cache a wwwroot enumeration/existence error as terminating. Avoid storing this class of failure in _initTerminatingError so a spurious failure does not require a full worker recycle to recover.

  3. (Optional) Guard the root itself. Add a Directory.Exists(FunctionAppRootPath) check so any future filesystem-visibility issue produces a clear, non-terminating diagnostic rather than an opaque enumeration exception.

Note: item (1) is the key change — it makes the worker functional for apps without a profile.ps1 even while running on an affected .NET 10 runtime (10.0.9 / 10.0.10), independent of the platform runtime update tracked in #1147. This gives us a worker-side fix we can ship without waiting on the platform.

Related

Environment

  • Worker: PowerShell 7.6 (.NET 10)
  • Unaffected: PowerShell 7.4 (.NET 8), 7.2
  • Exposure: apps whose wwwroot does not contain a profile.ps1

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions