Skip to content

Fix Windows install on PowerShell 5.1 (irm | iex and missing checksum sidecar) - #828

Merged
michaelneale merged 1 commit into
mainfrom
fix/windows-installer-iex-and-checksum
Jun 12, 2026
Merged

Fix Windows install on PowerShell 5.1 (irm | iex and missing checksum sidecar)#828
michaelneale merged 1 commit into
mainfrom
fix/windows-installer-iex-and-checksum

Conversation

@michaelneale

@michaelneale michaelneale commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator

Windows users can now install mesh-llm with the documented one-liner, and the install no longer dies when a release has no checksum sidecar.

What you can now do

On a stock Windows box (Windows PowerShell 5.1, the default), the documented command now works end to end:

irm https://meshllm.cloud/install.ps1 | iex

Previously this failed immediately with a ValidateSetFailure before any install logic ran, and even when invoked as a downloaded .ps1 it then hard-failed on releases that ship no .sha256 sidecar.

Bugs fixed

  1. irm | iex threw before running. The $Flavor parameter had [ValidateSet(...)] with an env-var default. Under Invoke-Expression, a [ValidateSet][string] parameter is initialised to "", which is immediately validated against the set, is not a member, and throws ValidateSetFailure. The [ValidateSet] is removed from the param; flavor values are still validated in Choose-Flavor against Get-SupportedFlavors, and MESH_LLM_INSTALL_FLAVOR is now resolved in the body. Behaviour for an explicit/invalid -Flavor is unchanged (still throws unsupported Windows flavor).

  2. Hard failure on a missing checksum sidecar (PowerShell 5.1). When a release has no <archive>.sha256, Invoke-WebRequest on Windows PowerShell 5.1 follows the GitHub releases/latest/download redirect and, on the 404 target, surfaces a response-less WebException (The request was aborted: The connection was closed unexpectedly.) rather than a clean 404. Test-MissingChecksumResponse returned $false for that, so the intended warn-and-continue path was unreachable and the install died with could not download checksum sidecar. A response-less WebException is now treated as a missing sidecar. MESH_LLM_REQUIRE_CHECKSUM=1 still hard-fails on a missing sidecar via the caller.

This keeps the existing installer policy: verify when the sidecar exists, warn and continue when it is missing, and fail only when a checksum is explicitly required.

Validation

Tested end to end on a Windows 11 desktop (Intel i5-12400F, Windows PowerShell 5.1) reached over Tailscale, against the current published release (which has no sidecars):

  • Before: irm | iex failed with ValidateSetFailure; running the file failed with could not download checksum sidecar.
  • After: detects the cuda flavor, downloads the bundle, warns Checksum sidecar not found; continuing without archive verification, extracts, updates the user Path, and prints mesh-llm 0.71.0.

Example output:

Installing flavor: cuda
Release channel: stable
Downloading https://github.com/Mesh-LLM/mesh-llm/releases/latest/download/mesh-llm-x86_64-pc-windows-msvc-cuda.zip
WARNING: Checksum sidecar not found; continuing without archive verification: ...zip.sha256
Added C:\Users\sshuser\AppData\Local\mesh-llm\bin to your user Path.
Installed mesh-llm-x86_64-pc-windows-msvc-cuda.zip to C:\Users\sshuser\AppData\Local\mesh-llm\bin
mesh-llm 0.71.0

Note: this PR only fixes installation. A separate issue tracks a runtime crash (STATUS_ILLEGAL_INSTRUCTION 0xC000001D) on CPUs without AVX-512 once inference runs; that is a build-flag fix in the native runtime, addressed separately.

Summary by CodeRabbit

  • Bug Fixes

    • Improved installation script compatibility with older Windows PowerShell versions
    • Enhanced checksum validation detection for legacy system environments
  • Chores

    • Refined installation parameter handling for improved consistency and reliability

… sidecar

The documented Windows install path (irm <url> | iex) failed before running
any install logic, and even with that fixed it then hard-failed when a release
had no .sha256 checksum sidecar.

- Remove [ValidateSet] from the $Flavor param. Under Invoke-Expression a
  [ValidateSet][string] param is initialised to "" and immediately validated,
  which is not in the set, so iex threw ValidateSetFailure before the script
  ran. Flavor values are still validated in Choose-Flavor against
  Get-SupportedFlavors, so behaviour is unchanged. The MESH_LLM_INSTALL_FLAVOR
  env default is now resolved in the body instead of the param block.

- Treat a response-less WebException as a missing checksum sidecar. On Windows
  PowerShell 5.1, Invoke-WebRequest follows the GitHub release redirect and, on
  a 404 target, surfaces a response-less WebException rather than a clean 404,
  so the intended warn-and-continue path was unreachable and the install died
  with 'could not download checksum sidecar'. A required checksum
  (MESH_LLM_REQUIRE_CHECKSUM=1) is still enforced by the caller.

Verified end-to-end on a Windows 11 box (PowerShell 5.1) over Tailscale:
irm | iex now downloads, warns about the missing sidecar, extracts, updates
PATH, and reports mesh-llm --version.
@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

install.ps1 refactors how the -Flavor parameter is validated and defaults from the environment, replacing built-in parameter constraints with conditional logic, and updates checksum-sidecar detection to handle PowerShell 5.1 WebException behavior for failed downloads.

Changes

Installer parameter and error handling updates

Layer / File(s) Summary
Flavor parameter validation and environment override
install.ps1
Removes ValidateSet constraint and automatic default from $Flavor parameter; adds conditional block to set $Flavor from $env:MESH_LLM_INSTALL_FLAVOR only when the parameter is not explicitly provided.
Checksum sidecar detection for PowerShell 5.1
install.ps1
Updates Test-MissingChecksumResponse to treat response-less download failures as missing checksum sidecars when the exception is a System.Net.WebException, with comments explaining PowerShell 5.1 redirect and 404 behavior.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: PowerShell 5.1 compatibility fixes for the Windows installer, specifically addressing irm|iex issues and checksum sidecar handling.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/windows-installer-iex-and-checksum

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@install.ps1`:
- Around line 309-318: Narrow the 5.1 workaround in Test-MissingChecksumResponse
so it only treats the specific response-less WebException that matches the
redirect→missing-asset symptom as “missing sidecar”: when
$ErrorRecord.Exception.Response is null AND $ErrorRecord.Exception -is
[System.Net.WebException], check the WebException.Status and message (e.g.
Status -eq [System.Net.WebExceptionStatus]::ConnectionClosed and the
Exception.Message contains "connection was closed unexpectedly" or similar
redirect/closed text) and return true only in that case; for any other
response-less WebException, do not treat it as a missing checksum (allow the
caller Assert-DownloadedFileChecksum to throw/handle normally). Reference
Test-MissingChecksumResponse, Assert-DownloadedFileChecksum, and
$ErrorRecord.Exception (System.Net.WebException).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e7ff68f7-5f59-49f6-abb7-0cde98dc5a6f

📥 Commits

Reviewing files that changed from the base of the PR and between 57a3c24 and 1ce3926.

📒 Files selected for processing (1)
  • install.ps1

Comment thread install.ps1
Comment on lines 309 to +318
$response = $ErrorRecord.Exception.Response
if (-not $response) {
return $false
# Windows PowerShell 5.1 follows the GitHub release redirect and then,
# on a 404 target, surfaces the failure as a response-less WebException
# ("The request was aborted: The connection was closed unexpectedly.")
# rather than a clean 404 HttpWebResponse. Treat a response-less
# WebException as a missing sidecar so the warn-and-continue path
# remains reachable on 5.1. A genuinely required checksum is still
# enforced by the caller via $RequireSidecar.
return $ErrorRecord.Exception -is [System.Net.WebException]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In Windows PowerShell 5.1, when Invoke-WebRequest follows a GitHub release redirect to a missing asset, what System.Net.WebException status/message shape is produced, and how does that differ from transient transport failures such as DNS lookup errors, TLS failures, proxy failures, or connection resets?

💡 Result:

When Invoke-WebRequest in Windows PowerShell 5.1 follows a GitHub release redirect and the final redirected URL resolves to a missing asset, the failure is treated as an HTTP error response (not a transport failure). The produced exception has: 1) Exception type / PowerShell error shape - CategoryInfo: InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest], WebException - FullyQualifiedErrorId: WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand [1][2] - The terminating error message shown is of the form: “Invoke-WebRequest: The remote server returned an error: (404) Not Found.” [1][2] 2) System.Net.WebException status / message shape - It is a WebException with Status = ProtocolError for HTTP protocol-level errors. [3][4] - The “response” is available on the exception (e.g., you can read $_.Exception.Response.StatusCode / StatusDescription in catch). [1][5] How this differs from transient transport failures - DNS lookup / name resolution failures: these fail before an HTTP response is received and correspond to WebExceptionStatus.NameResolutionFailure (different Status than ProtocolError). [6][7] - TLS / certificate validation failures: these correspond to WebExceptionStatus.TrustFailure or related secure-channel failures (different Status than ProtocolError). [3][4] - Proxy failures: they do not produce an HTTP protocol response; they correspond to other WebExceptionStatus values rather than ProtocolError (check WebException.Status). [6][4] - Connection resets / premature connection close: correspond to WebExceptionStatus.ConnectionClosed / similar statuses, not ProtocolError. [3][4] In short: a missing redirected GitHub asset produces a WebException shaped like an HTTP 404 (ProtocolError) with an error message indicating the HTTP status and with an attached Response object, whereas DNS/TLS/proxy/reset issues produce WebExceptions whose Status indicates the transport/root-cause (NameResolutionFailure, TrustFailure, ConnectionClosed, etc.) and do not present an HTTP status code from the server response. [1][3][4][7]

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate and print the exact blocks around the referenced lines and the related checksum helpers.
file="install.ps1"

echo "== install.ps1: lines 280-360 =="
nl -ba "$file" | sed -n '280,360p'

echo
echo "== Find Assert-DownloadedFileChecksum and related helpers =="
rg -n "Assert-DownloadedFileChecksum|Test-MissingChecksumResponse|\\$RequireSidecar|MESH_LLM_REQUIRE_CHECKSUM" "$file" || true

echo
echo "== Show Assert-DownloadedFileChecksum implementation =="
# Print a modest window around the function definition (avoid dumping huge files).
fn_line=$(rg -n "function\s+Assert-DownloadedFileChecksum" "$file" | head -n1 | cut -d: -f1 || true)
if [[ -n "${fn_line:-}" ]]; then
  start=$((fn_line-30)); end=$((fn_line+180))
  nl -ba "$file" | sed -n "${start},${end}p"
else
  echo "Function Assert-DownloadedFileChecksum not found in $file"
fi

echo
echo "== Show Test-MissingChecksumResponse implementation =="
fn_line=$(rg -n "function\s+Test-MissingChecksumResponse" "$file" | head -n1 | cut -d: -f1 || true)
if [[ -n "${fn_line:-}" ]]; then
  start=$((fn_line-30)); end=$((fn_line+180))
  nl -ba "$file" | sed -n "${start},${end}p"
else
  echo "Function Test-MissingChecksumResponse not found in $file"
fi

echo
echo "== Check for sidecar policy parity with install.sh and AGENTS.md =="
# Only search; don't assume paths. We'll try common filenames at repo root.
rg -n "require checksum|warn-and-continue|missing.*sidecar|MESH_LLM_REQUIRE_CHECKSUM" -S "AGENTS.md" "install.sh" 2>/dev/null || true
ls -la AGENTS.md install.sh 2>/dev/null || true

Repository: Mesh-LLM/mesh-llm

Length of output: 155


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="install.ps1"

echo "Repo files: "
ls -ლა . | head

echo
echo "== install.ps1 lines 280-360 (with line numbers via awk) =="
awk 'NR>=280 && NR<=360 {printf "%d\t%s\n", NR, $0}' "$file"

echo
echo "== Locate checksum helpers and related vars =="
rg -n "Assert-DownloadedFileChecksum|Test-MissingChecksumResponse|\$RequireSidecar|MESH_LLM_REQUIRE_CHECKSUM|warn-and-continue|sidecar" "$file" || true

echo
echo "== Show Assert-DownloadedFileChecksum implementation =="
start=$(rg -n "function[[:space:]]+Assert-DownloadedFileChecksum" "$file" | head -n1 | cut -d: -f1 || true)
if [[ -n "${start:-}" ]]; then
  end=$((start+220))
  awk -v s="$start" -v e="$end" 'NR>=s && NR<=e {printf "%d\t%s\n", NR, $0}' "$file"
else
  echo "Function Assert-DownloadedFileChecksum not found."
fi

echo
echo "== Show Test-MissingChecksumResponse implementation =="
start=$(rg -n "function[[:space:]]+Test-MissingChecksumResponse" "$file" | head -n1 | cut -d: -f1 || true)
if [[ -n "${start:-}" ]]; then
  end=$((start+140))
  awk -v s="$start" -v e="$end" 'NR>=s && NR<=e {printf "%d\t%s\n", NR, $0}' "$file"
else
  echo "Function Test-MissingChecksumResponse not found."
fi

echo
echo "== Inspect AGENTS.md and install.sh for warn-and-continue / checksum policy =="
ls -la AGENTS.md install.sh 2>/dev/null || true

rg -n "warn-and-continue|missing.*sidecar|RequireSidecar|MESH_LLM_REQUIRE_CHECKSUM|checksum" AGENTS.md install.sh 2>/dev/null || true

Repository: Mesh-LLM/mesh-llm

Length of output: 156


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Find install.ps1 paths =="
fd -t f "install.ps1" . || true

echo
echo "== List repo root for likely policy files =="
ls -la .

echo
echo "== Show AGENTS.md / install.sh if present =="
ls -la AGENTS.md install.sh 2>/dev/null || true

echo
echo "== Locate referenced line block in install.ps1 =="
# If multiple install.ps1 exist, dump lines around 309-330 for each.
mapfile -t files < <(fd -t f "install.ps1" . || true)
if [[ ${`#files`[@]} -eq 0 ]]; then
  echo "No install.ps1 found."
  exit 0
fi

for f in "${files[@]}"; do
  echo
  echo "---- $f (lines 295-340) ----"
  awk 'NR>=295 && NR<=340 {printf "%d\t%s\n", NR, $0}' "$f" | head -n 200
done

echo
echo "== Locate checksum helpers and missing-sidecar predicates in install.ps1 =="
for f in "${files[@]}"; do
  echo
  echo "---- $f symbols ----"
  rg -n "Assert-DownloadedFileChecksum|Test-MissingChecksumResponse|\$RequireSidecar|RequireSidecar|MESH_LLM_REQUIRE_CHECKSUM|missing.*sidecar|warn-and-continue" "$f" || true
done

echo
echo "== Show implementations for Assert-DownloadedFileChecksum + Test-MissingChecksumResponse (if found) =="
for f in "${files[@]}"; do
  fn1=$(rg -n "function[[:space:]]+Assert-DownloadedFileChecksum" "$f" | head -n1 | cut -d: -f1 || true)
  if [[ -n "${fn1:-}" ]]; then
    echo
    echo "---- $f: Assert-DownloadedFileChecksum around line $fn1 ----"
    start=$((fn1-20)); end=$((fn1+180))
    awk -v s="$start" -v e="$end" 'NR>=s && NR<=e {printf "%d\t%s\n", NR, $0}' "$f"
  fi

  fn2=$(rg -n "function[[:space:]]+Test-MissingChecksumResponse" "$f" | head -n1 | cut -d: -f1 || true)
  if [[ -n "${fn2:-}" ]]; then
    echo
    echo "---- $f: Test-MissingChecksumResponse around line $fn2 ----"
    start=$((fn2-20)); end=$((fn2+200))
    awk -v s="$start" -v e="$end" 'NR>=s && NR<=e {printf "%d\t%s\n", NR, $0}' "$f"
  fi
done

echo
echo "== Policy parity checks (AGENTS.md / install.sh) =="
for target in AGENTS.md install.sh; do
  if [[ -f "$target" ]]; then
    echo
    echo "---- $target ----"
    rg -n "warn-and-continue|missing.*sidecar|RequireSidecar|MESH_LLM_REQUIRE_CHECKSUM|checksum" "$target" || true
  fi
done

Repository: Mesh-LLM/mesh-llm

Length of output: 2975


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== install.ps1: lines 300-340 =="
awk 'NR>=300 && NR<=340 {printf "%d\t%s\n", NR, $0}' install.ps1

echo
echo "== install.ps1: locate missing-sidecar / checksum helpers =="
rg -n "Assert-DownloadedFileChecksum|Test-MissingChecksumResponse|RequireSidecar|MESH_LLM_REQUIRE_CHECKSUM|missing.*sidecar|warn-and-continue|checksum" install.ps1

echo
echo "== install.ps1: show Assert-DownloadedFileChecksum function body (small window) =="
fn=$(rg -n "function[[:space:]]+Assert-DownloadedFileChecksum" install.ps1 | head -n1 | cut -d: -f1 || true)
if [[ -n "${fn:-}" ]]; then
  start=$((fn-10)); end=$((fn+220))
  awk -v s="$start" -v e="$end" 'NR>=s && NR<=e {printf "%d\t%s\n", NR, $0}' install.ps1
else
  echo "Not found"
fi

echo
echo "== install.ps1: show Test-MissingChecksumResponse function body (small window) =="
fn=$(rg -n "function[[:space:]]+Test-MissingChecksumResponse" install.ps1 | head -n1 | cut -d: -f1 || true)
if [[ -n "${fn:-}" ]]; then
  start=$((fn-10)); end=$((fn+220))
  awk -v s="$start" -v e="$end" 'NR>=s && NR<=e {printf "%d\t%s\n", NR, $0}' install.ps1
else
  echo "Not found"
fi

echo
echo "== AGENTS.md: find warn-and-continue / checksum policy =="
rg -n "warn-and-continue|missing.*sidecar|RequireSidecar|MESH_LLM_REQUIRE_CHECKSUM|checksum|sidecar" AGENTS.md || true

echo
echo "== install.sh: find matching checksum/sidecar policy =="
rg -n "warn-and-continue|missing.*sidecar|RequireSidecar|MESH_LLM_REQUIRE_CHECKSUM|checksum|sidecar" install.sh || true

Repository: Mesh-LLM/mesh-llm

Length of output: 19252


Narrow “missing checksum sidecar” handling for response-less WebException

install.ps1’s Test-MissingChecksumResponse treats any response-less System.Net.WebException ($ErrorRecord.Exception.Response is null ⇒ -is [System.Net.WebException]) as “missing sidecar”, and Assert-DownloadedFileChecksum then warns and returns, skipping SHA-256 verification unless $RequireSidecar/$RequireChecksum is set. This can downgrade transient transport failures (DNS/TLS/proxy/connection resets) into an integrity-unverified install, which diverges from AGENTS.md/install.sh’s warn-and-continue policy for actually missing .sha256 assets (HTTP 404/410).

Update the 5.1 workaround to gate on the specific WebException shape/status/message that corresponds to the redirect→missing-asset behavior described in the comment, and throw for other response-less transport failures instead of accepting all WebExceptions.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@install.ps1` around lines 309 - 318, Narrow the 5.1 workaround in
Test-MissingChecksumResponse so it only treats the specific response-less
WebException that matches the redirect→missing-asset symptom as “missing
sidecar”: when $ErrorRecord.Exception.Response is null AND
$ErrorRecord.Exception -is [System.Net.WebException], check the
WebException.Status and message (e.g. Status -eq
[System.Net.WebExceptionStatus]::ConnectionClosed and the Exception.Message
contains "connection was closed unexpectedly" or similar redirect/closed text)
and return true only in that case; for any other response-less WebException, do
not treat it as a missing checksum (allow the caller
Assert-DownloadedFileChecksum to throw/handle normally). Reference
Test-MissingChecksumResponse, Assert-DownloadedFileChecksum, and
$ErrorRecord.Exception (System.Net.WebException).

@michaelneale
michaelneale merged commit cb3a818 into main Jun 12, 2026
21 checks passed
@michaelneale
michaelneale deleted the fix/windows-installer-iex-and-checksum branch June 12, 2026 01:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant