Skip to content

Windows container: job — container PATH dropped after a step writes $GITHUB_PATH (shell fails to launch, exit 126/127) #9

Description

@bugale

Summary

In a Windows job-level container: job, as soon as any step writes to $GITHUB_PATH (directly, or via a setup-* action), every subsequent in-container step fails to launch its shell:

container <id> encountered an error during hcs::System::CreateProcess: powershell ...:
The system cannot find the file specified. (0x2)

and the step exits 126/127.

Root cause

When a step appends to $GITHUB_PATH, ContainerStepHost.ExecuteAsync re-runs the in-container command with an explicit PATH override:

docker exec ... -e PATH="<prepended-dirs>:<ContainerRuntimePath>" <id> <shell> ...

ContainerRuntimePath is captured once at container start in ContainerOperationProvider from
docker inspect --format "{{range .Config.Env}}{{println .}}{{end}}" <id>, parsed by DockerUtil.ParsePathFromConfigEnv.

Two problems surface on Windows:

  1. .Config.Env has no PATH. Typical Windows images (mcr.microsoft.com/windows/servercore, nanoserver, even the dotnet images) do not declare Path/PATH in their image config. A Windows container's real PATH (System32, PowerShell, …) is provided by the OS at runtime and is not visible via docker inspect .Config.Env. So ParsePathFromConfigEnv returns empty and ContainerRuntimePath is empty.
  2. POSIX separator. The override joins the prepended dirs and the base with a hard-coded :, which is wrong on Windows (separator is ;, and drive letters contain :).

With an empty base, the override collapses to -e PATH="<prepended-dirs>" — the container's System32/PowerShell are gone, so the next step's shell can't be found.

Reproduction

docker inspect <id> --format "{{range .Config.Env}}{{println .}}{{end}}" shows no PATH, while docker exec <id> cmd /c echo %PATH% shows the real one. Replaying the exact command line the runner produces against a real Windows container:

# broken (empty base — what the runner emits today):
docker exec -e PATH="C:\probe" <id> powershell -NoProfile -Command "echo hi"
#   -> hcs::System::CreateProcess ...: The system cannot find the file specified. (0x2), exit 127

# with the container's real PATH appended and ';' separator:
docker exec -e PATH="C:\probe;C:\Windows\system32;C:\Windows;..." <id> powershell -NoProfile -Command "echo hi"
#   -> hi, exit 0

Minimal workflow that triggers it:

jobs:
  repro:
    runs-on: [self-hosted, windows]
    container: mcr.microsoft.com/windows/servercore:ltsc2025
    defaults: { run: { shell: powershell } }
    steps:
      - run: Add-Content -Path $env:GITHUB_PATH -Value 'C:\probe'
      - run: Write-Host "next step's shell launches: $env:PATH"   # fails today

Suggested fix

  • In ContainerOperationProvider, when .Config.Env yields no PATH on Windows, read the container's real PATH (docker exec <id> cmd /c echo %PATH%).
  • In ContainerStepHost.ExecuteAsync, join with Path.PathSeparator instead of :.

Small patch (2 files, ~9 lines); PR to follow.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions