jackwener/OpenCLI · error · CommandExecutionError

${label} returned HTTP ${resp.status}

Error message

${label} returned HTTP ${resp.status}

What it means

gemsFetch (clis/rubygems/utils.js:68) wraps every RubyGems.org REST call. It explicitly handles 404 (EmptyResultError) and 429 (rate-limit hint), and any other non-OK status falls into this generic CommandExecutionError carrying the raw HTTP status in the message. It signals that rubygems.org answered but with an unhandled error status (e.g. 500, 503, 403).

Source

Thrown at clis/rubygems/utils.js:68

        resp = await fetch(url, { headers: { 'user-agent': UA, accept: 'application/json' } });
    }
    catch (err) {
        throw new CommandExecutionError(
            `${label} request failed: ${err?.message ?? err}`,
            'Check that rubygems.org is reachable from this network.',
        );
    }
    if (resp.status === 404) {
        throw new EmptyResultError(label, `RubyGems returned 404 for ${url}.`);
    }
    if (resp.status === 429) {
        throw new CommandExecutionError(
            `${label} returned HTTP 429 (rate limited)`,
            'RubyGems throttles bursts; wait a few seconds and retry.',
        );
    }
    if (!resp.ok) {
        throw new CommandExecutionError(`${label} returned HTTP ${resp.status}`);
    }
    let body;
    try {
        body = await resp.json();
    }
    catch (err) {
        throw new CommandExecutionError(`${label} returned malformed JSON: ${err?.message ?? err}`);
    }
    return body;
}

/** Trim "2026-03-24T20:27:42.098Z" → "2026-03-24T20:27:42Z" so timestamps share a uniform precision. */
export function trimDate(value) {
    const s = String(value ?? '').trim();
    if (!s) return null;
    const noFrac = s.replace(/\.\d+/, '');
    return noFrac.endsWith('Z') ? noFrac : `${noFrac}Z`;
}

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Re-run the command after a short delay — 5xx statuses from rubygems.org are usually transient.
  2. Check https://status.rubygems.org for an ongoing outage.
  3. If you get 403/407, check proxy/VPN configuration and that rubygems.org is reachable (curl the URL directly).
  4. Retry with a different network (e.g. disable corporate proxy) to rule out network interception.
  5. Capture the exact HTTP status in the message and consult RubyGems API docs for that status code.

Example fix

// before
await gemsFetch(`${GEMS_BASE}/gems/${name}.json`, 'rubygems gem info');
// after
try {
  const info = await gemsFetch(`${GEMS_BASE}/gems/${name}.json`, 'rubygems gem info');
} catch (err) {
  if (/returned HTTP 5\d\d/.test(err.message)) {
    // retry with backoff or check status.rubygems.org
  }
}
Defensive patterns

Strategy: retry

Validate before calling

// Pre-check service health before batch calls
const health = await fetch('https://rubygems.org/api/v1/versions/rails.json');
if (!health.ok && health.status >= 500) throw new Error('rubygems.org is unhealthy; defer batch');

Type guard

function isUnhandledHttpStatus(err) {
  return err instanceof Error && /returned HTTP \d{3}/.test(err.message) && !/HTTP (404|429)/.test(err.message);
}

Try / catch

try {
  const info = await gemsFetch(url, 'rubygems gem info');
} catch (err) {
  if (/returned HTTP 5\d\d/.test(err.message)) {
    await sleep(2000);
    return gemsFetch(url, 'rubygems gem info'); // retry with backoff
  }
  throw err;
}

Prevention

When it happens

Trigger: Any gemsFetch call (gem info, versions, downloads, search) where the fetch succeeded but resp.ok is false and the status is neither 404 nor 429 — e.g. rubygems.org returning 5xx during an outage, a 403 from a proxy/firewall, or a 410 for removed endpoints.

Common situations: RubyGems.org incidents/degraded service; corporate proxies or VPNs intercepting requests and returning 403/502; rubygems.org temporarily blocking a user-agent or IP range; transient 502/503 from the CDN in front of rubygems.org.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/259239ef2740f608. Report an issue: GitHub.