install.sh fails with 'Could not find Codex package or platform npm release assets' on valid release

Open 💬 2 comments Opened Jul 8, 2026 by sebuh-infsol

What happened

Running the official install script fails even though the release and its assets exist:

$ sh -c 'curl -fsSL https://chatgpt.com/codex/install.sh | CODEX_NON_INTERACTIVE=1 sh'
Could not find Codex package or platform npm release assets for Codex 0.143.0.

The release rust-v0.143.0 exists at https://github.com/openai/codex/releases/tag/rust-v0.143.0 and the asset codex-package-x86_64-unknown-linux-musl.tar.gz is present.

Root cause

The install script at https://chatgpt.com/codex/install.sh uses a hand-rolled awk-based JSON parser (function release_asset_digest_or_empty, lines 169-205) to extract asset digests from the GitHub API response. This parser is fragile:

  1. It tracks brace depth using a depth variable that is never initialized (awk defaults to 0, but the logic is still brittle).
  2. The GitHub API returns a deeply nested JSON structure with nested objects (uploader, label, etc.) that the simple {/} depth counter cannot reliably parse.
  3. If the API response is malformed, truncated, or the parser fails to match, it silently returns empty — causing release_asset_exists to return false.

The script then falls through to the else branch at line 859-861:

else
  echo "Could not find Codex package or platform npm release assets for Codex $resolved_version." >&2
  exit 1
fi

Reproduction

curl -fsSL https://api.github.com/repos/openai/codex/releases/latest | \
  awk -v asset="codex-package-x86_64-unknown-linux-musl.tar.gz" '
    /"name":[[:space:]]*"[^"]+"/ {
      name = $0
      sub(/^.*"name":[[:space:]]*"/, "", name)
      sub(/".*$/, "", name)
      if (name == asset) {
        in_asset = 1
        asset_depth = depth
      }
    }
    in_asset && /"digest":[[:space:]]*"[^"]+"/ {
      digest = $0
      sub(/^.*"digest":[[:space:]]*"/, "", digest)
      sub(/".*$/, "", digest)
    }
    {
      line = $0
      opens = gsub(/\{/, "{", line)
      closes = gsub(/\}/, "}", line)
      depth += opens - closes
      if (in_asset && depth < asset_depth) {
        in_asset = 0
      }
    }
    END {
      if (digest != "") { print digest }
    }'

This returns empty because the awk parser cannot correctly navigate the nested JSON.

Suggested fix

Replace the awk-based JSON parser with jq (if available) or a more robust parsing approach. At minimum, fall back to checking browser_download_url existence via a simple grep if the digest lookup fails, since the presence of any URL is sufficient to confirm the asset exists.

Alternatively, use curl -fsSL HEAD requests to check asset availability directly:

release_asset_exists() {
  asset="$1"
  url="https://github.com/openai/codex/releases/download/rust-v${resolved_version}/${asset}"
  curl -fsSL -o /dev/null "$url"
}

This avoids JSON parsing entirely and is much more reliable.

Environment

  • OS: Debian 13 (bookworm), x86_64
  • Shell: sh (dash)
  • curl: available
  • jq: not available on all systems
  • Current codex version: 0.142.0
  • Target version: 0.143.0

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗