You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
getting-started.yml's step 7, install the Zephyr SDK (west sdk install, the printed remedy), fails on every PR that reaches it. The message it prints blames the pinned version:
FATAL ERROR: Unavailable SDK version: 1.0.1.Please select from the list below:
##[error]west sdk install failed 3 times -- this is a real failure, not a transient fetch.
The pin is fine. GET /repos/zephyrproject-rtos/sdk-ng/releases is returning an empty array, west sdk install resolves --version against that list, and it reports an empty list the same way it reports a genuinely absent version. Note the blank line after Please select from the list below: in the log — the list it offers is empty. That blank line is the whole diagnosis.
This is upstream/GitHub-side, not a tan-cli defect and not the pin's fault. Filed because the failure is currently indistinguishable from "our pin is wrong", and because the same command is the remedy tan doctor prints to customers.
Evidence
Observed on run 32039026116 (PR #839, branch fix/813-fold-752-and-gate-fragments, 2026-08-17). The retry loop at getting-started.yml:536-549 burned all three attempts in 50 seconds, each one fetching the list in ~1.3 s and failing immediately:
14:30:05.9961750Z FATAL ERROR: Unavailable SDK version: 1.0.1.Please select from the list below:
14:30:06.0258117Z ##[warning]west sdk install attempt 1/3 failed; retrying in 15s
14:30:21.3205938Z Fetching Zephyr SDK list...
14:30:22.6511747Z FATAL ERROR: Unavailable SDK version: 1.0.1.Please select from the list below:
14:30:22.6807951Z ##[warning]west sdk install attempt 2/3 failed; retrying in 30s
14:30:52.9685789Z Fetching Zephyr SDK list...
14:30:56.6586055Z FATAL ERROR: Unavailable SDK version: 1.0.1.Please select from the list below:
14:30:56.6879199Z ##[error]west sdk install failed 3 times -- this is a real failure, not a transient fetch.
Fast and empty, three times. Not a slow or partial fetch.
Not a rate limit, which is what this step's own comment predicts.getting-started.yml:490-501 documents the fetch_releases API rate limit exceeded failure mode and passes GH_TOKEN (line 505) to lift it. Measured from a workstation with a fully authenticated, unthrottled token:
$ gh api rate_limit --jq '.resources.core'{"limit":5000,"remaining":4985,"reset":1786978845,"used":15}
The release, the tag and the assets are all alive. Only the list endpoint is empty. Same token, same minute:
$ gh api "repos/zephyrproject-rtos/sdk-ng/releases?per_page=100" --jq 'length'0
$ gh api "repos/zephyrproject-rtos/sdk-ng/releases?per_page=100"[]
$ gh api repos/zephyrproject-rtos/sdk-ng/releases/latest --jq '.tag_name, .published_at'v1.0.12026-03-25T02:04:03Z
$ gh api "repos/zephyrproject-rtos/sdk-ng/tags?per_page=10" --jq '.[].name'v1.0.1v1.0.0v1.0.0-rc1v1.0.0-beta1v0.18.0-alpha4v0.18.0-alpha3v0.18.0-alpha2v0.18.0-alpha1v0.17.4v0.17.4-rc1
The release assets download by direct URL, HTTP 200, while the list is empty:
versionDerivation in that file states the pin is re-derived, not hand-copied: "Equal to the SDK_VERSION file at the Zephyr revision metadata/bootstrap.json's zephyr.version pins (currently v4.4.1) -- verified with git show <zephyr.version>:SDK_VERSION against the Zephyr checkout, not copied by hand. scripts/check_toolchain_lock.py re-derives and asserts this at every run." So there is no wrong-pin hypothesis left to chase.
Time-bounded, which rules out "always broken". Same pin, same workflow, same day:
The last 12 getting-started runs are all success except the 14:26 one. Nothing in #839's diff reaches this step — it touches .github/workflows/release.yml, CHANGELOG.md, changelog.d/752.fixed.md and changelog.d/813.fixed.md only.
Impact
CI, now: every PR that reaches step 7 goes red until the upstream list repopulates. getting-started is not a required context (the five are seam1 -- plan-shape parity, the three python -- pytest across python/ legs, and zizmor · workflow security), so this does not hard-block merges — it makes them UNSTABLE and adds one red check that every reviewer now has to re-diagnose from scratch, because the message names our pin.
Customers, worse:west sdk install is not just a CI step, it is the remedy tan doctorprints. getting-started.yml:24 says so:
# west sdk install <- the remedy tan's own build-readiness check PRINTS
While the list endpoint is empty, that printed remedy fails for every user on every network — a home IP with its own unauthenticated quota included, since this is not a quota problem — and the error tells them their SDK version is unavailable. The predictable response is to start editing a pin that is correct.
The retry loop cannot help and says the opposite. Its own text asserts this is a real failure, not a transient fetch. That is accurate and still misleading: real, not transient, and not ours. Three attempts over 50 s cannot outlast an upstream list outage.
Suggested fix
Two separable pieces. The first is worth doing regardless of when upstream recovers, because it is about making the red readable:
Distinguish "list empty" from "version absent" before calling west sdk install. One gh api probe in the step: if /releases is empty while /releases/latest resolves, the list is broken, not the pin — emit ::warning:: naming the upstream outage and skip or soft-fail rather than printing a message that accuses the pin. The house rule this repo already applied to sdk list --online in clean-host.yml covers exactly this case: "A gate that goes red for a reason unrelated to its subject stops being read."
A list-independent acquisition path. The pinned alp-sdk already carries everything needed — baseUrl, per-host filename, sizeBytes and sha256 for all six artifacts (linux-x86_64, windows-x86_64, macos-aarch64 x minimal-sdk, arm-zephyr-eabi-toolchain) — and the direct URLs serve HTTP 200 right now. bootstrap acquires the arm-zephyr-eabi cross toolchain (ADR 0021 Lane 1 P1) #474 (bootstrap acquires the arm-zephyr-eabi cross toolchain, ADR 0021 Lane 1 P1) is the existing home for this; this outage is a concrete argument for it, since a metadata-driven download with a sha256 check does not depend on the release-list API at all.
Deliberately not proposed: bumping or pinning a different zephyrSdk.version. Nothing is wrong with 1.0.1, every other version would resolve against the same empty list, and the value is re-derived upstream by scripts/check_toolchain_lock.py.
Verifying recovery
$ gh api "repos/zephyrproject-rtos/sdk-ng/releases?per_page=100" --jq 'length'
Non-zero means upstream is back and this failure mode disappears on its own. That is the reason to keep item 1 above: the next occurrence should be legible without repeating this whole investigation.
Problem
getting-started.yml's step 7,install the Zephyr SDK (west sdk install, the printed remedy), fails on every PR that reaches it. The message it prints blames the pinned version:The pin is fine.
GET /repos/zephyrproject-rtos/sdk-ng/releasesis returning an empty array,west sdk installresolves--versionagainst that list, and it reports an empty list the same way it reports a genuinely absent version. Note the blank line afterPlease select from the list below:in the log — the list it offers is empty. That blank line is the whole diagnosis.This is upstream/GitHub-side, not a tan-cli defect and not the pin's fault. Filed because the failure is currently indistinguishable from "our pin is wrong", and because the same command is the remedy
tan doctorprints to customers.Evidence
Observed on run 32039026116 (PR #839, branch
fix/813-fold-752-and-gate-fragments, 2026-08-17). The retry loop atgetting-started.yml:536-549burned all three attempts in 50 seconds, each one fetching the list in ~1.3 s and failing immediately:Fast and empty, three times. Not a slow or partial fetch.
Not a rate limit, which is what this step's own comment predicts.
getting-started.yml:490-501documents thefetch_releases API rate limit exceededfailure mode and passesGH_TOKEN(line 505) to lift it. Measured from a workstation with a fully authenticated, unthrottled token:The release, the tag and the assets are all alive. Only the list endpoint is empty. Same token, same minute:
The release assets download by direct URL, HTTP 200, while the list is empty:
Both redirect to a signed
release-assets.githubusercontent.comURL and serve.The version is not hardcoded here.
getting-started.yml:511reads it out of the pinned alp-sdk:version="$(python3 -c 'import json; print(json.load(open("alp-sdk/metadata/toolchains.json"))["zephyrSdk"]["version"])')"PINNED_SDK_TAGis88318e759958529fbbd8fe9d481373681c0fa78d(read fromparity.yml, pergetting-started.yml:305-316), and at that ref:versionDerivationin that file states the pin is re-derived, not hand-copied: "Equal to theSDK_VERSIONfile at the Zephyr revision metadata/bootstrap.json'szephyr.versionpins (currently v4.4.1) -- verified withgit show <zephyr.version>:SDK_VERSIONagainst the Zephyr checkout, not copied by hand. scripts/check_toolchain_lock.py re-derives and asserts this at every run." So there is no wrong-pin hypothesis left to chase.Time-bounded, which rules out "always broken". Same pin, same workflow, same day:
The last 12
getting-startedruns are allsuccessexcept the 14:26 one. Nothing in #839's diff reaches this step — it touches.github/workflows/release.yml,CHANGELOG.md,changelog.d/752.fixed.mdandchangelog.d/813.fixed.mdonly.Impact
CI, now: every PR that reaches step 7 goes red until the upstream list repopulates.
getting-startedis not a required context (the five areseam1 -- plan-shape parity, the threepython -- pytest across python/legs, andzizmor · workflow security), so this does not hard-block merges — it makes themUNSTABLEand adds one red check that every reviewer now has to re-diagnose from scratch, because the message names our pin.Customers, worse:
west sdk installis not just a CI step, it is the remedytan doctorprints.getting-started.yml:24says so:While the list endpoint is empty, that printed remedy fails for every user on every network — a home IP with its own unauthenticated quota included, since this is not a quota problem — and the error tells them their SDK version is unavailable. The predictable response is to start editing a pin that is correct.
The retry loop cannot help and says the opposite. Its own text asserts
this is a real failure, not a transient fetch. That is accurate and still misleading: real, not transient, and not ours. Three attempts over 50 s cannot outlast an upstream list outage.Suggested fix
Two separable pieces. The first is worth doing regardless of when upstream recovers, because it is about making the red readable:
Distinguish "list empty" from "version absent" before calling
west sdk install. Onegh apiprobe in the step: if/releasesis empty while/releases/latestresolves, the list is broken, not the pin — emit::warning::naming the upstream outage and skip or soft-fail rather than printing a message that accuses the pin. The house rule this repo already applied tosdk list --onlineinclean-host.ymlcovers exactly this case: "A gate that goes red for a reason unrelated to its subject stops being read."A list-independent acquisition path. The pinned alp-sdk already carries everything needed —
baseUrl, per-hostfilename,sizeBytesandsha256for all six artifacts (linux-x86_64,windows-x86_64,macos-aarch64xminimal-sdk,arm-zephyr-eabi-toolchain) — and the direct URLs serve HTTP 200 right now. bootstrap acquires the arm-zephyr-eabi cross toolchain (ADR 0021 Lane 1 P1) #474 (bootstrap acquires the arm-zephyr-eabi cross toolchain, ADR 0021 Lane 1 P1) is the existing home for this; this outage is a concrete argument for it, since a metadata-driven download with asha256check does not depend on the release-list API at all.Deliberately not proposed: bumping or pinning a different
zephyrSdk.version. Nothing is wrong with1.0.1, every other version would resolve against the same empty list, and the value is re-derived upstream byscripts/check_toolchain_lock.py.Verifying recovery
$ gh api "repos/zephyrproject-rtos/sdk-ng/releases?per_page=100" --jq 'length'Non-zero means upstream is back and this failure mode disappears on its own. That is the reason to keep item 1 above: the next occurrence should be legible without repeating this whole investigation.