From cb1acdf4c6ea16dcdb673de31ed8cd209462a3a2 Mon Sep 17 00:00:00 2001 From: MickLesk <47820557+MickLesk@users.noreply.github.com> Date: Thu, 8 Oct 2026 09:45:58 +0200 Subject: [PATCH 1/2] Retry a release list that arrives cut short The list request in _forge_release_json used a bare curl whose exit status was dropped. A body cut short still reports 200, jq then failed on the broken JSON with its error hidden, and the selection came back empty: Jellyfin's update stopped with "intel/compute-runtime has no stable release to install" (exit 250) while the release was there (ProxmoxVE#17757). An incomplete or unparsable 200 now counts as a failed attempt, so it is retried and, if it keeps failing, reported as a connection error. --- lib/forge.func | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/forge.func b/lib/forge.func index 0f36081..dfe1659 100644 --- a/lib/forge.func +++ b/lib/forge.func @@ -1473,7 +1473,7 @@ _forge_release_json() { fi local -a timeouts=(60 120 240) - local raw attempt=1 delay=2 code ok=false + local raw attempt=1 delay=2 code ok=false curl_rc raw="$(mktemp /tmp/tools-forge-rel-XXXXXX)" || return 73 local -a headers=() @@ -1485,13 +1485,18 @@ _forge_release_json() { [[ -n "$auth" ]] && headers+=(-H "$auth") while ((attempt <= ${#timeouts[@]})); do + curl_rc=0 code="$(curl -sSL -w "%{http_code}" -o "$raw" \ --max-time "${timeouts[$((attempt - 1))]}" \ - "${headers[@]}" "$api_url" 2>/dev/null)" || true + "${headers[@]}" "$api_url" 2>/dev/null)" || curl_rc=$? if [[ "$code" == "200" ]]; then - ok=true - break + # A body cut short still reports 200, and jq then finds no release at all. + if ((curl_rc == 0)) && jq -e 'type == "array" or type == "object"' "$raw" >/dev/null 2>&1; then + ok=true + break + fi + code="000" fi if [[ "$forge" == "github" && "$code" == "401" ]]; then From 2f596a92e9a03c2b34c86531ef0ebe879da04655 Mon Sep 17 00:00:00 2001 From: MickLesk <47820557+MickLesk@users.noreply.github.com> Date: Thu, 8 Oct 2026 09:45:59 +0200 Subject: [PATCH 2/2] Clear a handled silent failure on the next silent call silent() leaves the failed command in _SILENT_FAILED_* for the error handler. When the caller handled that failure itself, as retry loops do, the values stayed, and any later unrelated error was reported as that old command. Start every call with a clean slate. --- core/core.func | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/core.func b/core/core.func index 4f71369..5be2adc 100644 --- a/core/core.func +++ b/core/core.func @@ -928,6 +928,8 @@ log_section() { # - Temporarily disables error trap to capture exit code correctly # ------------------------------------------------------------------------------ silent() { + # A failure the caller handled (a retry loop) must not be reported for a later, unrelated error. + unset _SILENT_FAILED_RC _SILENT_FAILED_CMD _SILENT_FAILED_LINE _SILENT_FAILED_LOG local cmd="$*" local caller_line="${BASH_LINENO[0]:-unknown}" local logfile="$(get_active_logfile)"