jackwener/OpenCLI · error · CommandExecutionError

returned HTTP .

Error message

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

What it means

wttrFetch in clis/wttr/utils.js wraps all HTTP calls to wttr.in. After handling fetch network failures and 404s separately, any other non-OK HTTP status (429 rate limit, 5xx server errors, etc.) is raised as a CommandExecutionError with the upstream status code in the message. It signals the wttr.in service itself rejected or failed the request, not that the location was unknown.

Solutions

  1. Retry the request after a short backoff (wttr.in 429/5xx are usually transient).
  2. If the status is 429, slow down request frequency or cache results instead of re-fetching.
  3. Check https://wttr.in directly in a browser to confirm the service is up.
  4. Check local network/proxy configuration that might inject non-200 responses.
  5. Use an alternative weather source (e.g. the NWS CLI for US locations) if wttr.in stays down.

Example fix

// before
const weather = await body('--location', 'Berlin');
// after
try {
  const weather = await body('--location', 'Berlin');
} catch (err) {
  if (/returned HTTP (429|5\d\d)/.test(err.message)) {
    await new Promise(r => setTimeout(r, 5000));
    // retry once
  } else throw err;
}
Defensive patterns

Strategy: retry

Validate before calling

const resp = await fetch('https://wttr.in/Berlin?format=j1');
if (!resp.ok && resp.status !== 404) {
  console.warn(`wttr.in unhealthy (HTTP ${resp.status}), use cached data or another source`);
}

Try / catch

try {
  const weather = await body('--location', loc);
} catch (err) {
  if (/returned HTTP (429|5\d\d)/.test(err.message)) {
    await sleep(backoff);
    // retry with exponential backoff, fall back to cached data on final failure
  } else throw err;
}

Prevention

When it happens

Trigger: Calling wttrFetch (via the wttr CLI's body command) when wttr.in responds with a status other than 200 or 404 — e.g. HTTP 429 when rate-limited, 500/502/503 during wttr.in outages or overload.

Common situations: Hammering wttr.in with many rapid requests (it rate-limits aggressively); wttr.in's shared public service being overloaded or down; corporate proxies intercepting the request and returning 403/502; IPv6 connectivity issues causing upstream gateway errors.

Related errors


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

Appendix: source

Thrown at clis/wttr/utils.js:31

        throw new ArgumentError(`--${name} is required`);
    }
    return value.trim();
}

export async function wttrFetch(location, label) {
    // wttr.in path-encodes the location. Spaces → %20 is fine; commas survive.
    const url = `${WTTR_BASE}/${encodeURIComponent(location)}?format=j1`;
    let resp;
    try {
        resp = await fetch(url, { headers: { 'User-Agent': UA, accept: 'application/json' } });
    } catch (err) {
        throw new CommandExecutionError(`${label} request failed: ${err.message}`);
    }
    if (resp.status === 404) {
        throw new EmptyResultError(label, `${label} could not find location "${location}".`);
    }
    if (!resp.ok) {
        throw new CommandExecutionError(`${label} returned HTTP ${resp.status}.`);
    }
    let body;
    try {
        body = await resp.json();
    } catch (err) {
        // wttr.in falls back to plain-text "Unknown location" for some bad inputs;
        // promote that to EmptyResult instead of pretending we got JSON.
        throw new EmptyResultError(label, `${label} returned non-JSON body (likely unknown location).`);
    }
    return body;
}

// wttr.in's "weatherDesc" / "lang_en" fields are arrays of `{ value: '...' }` objects.
// Single-element 99% of the time but the schema is a list.
export function pickWeatherDesc(arr) {
    if (!Array.isArray(arr) || !arr.length) return '';
    const first = arr[0];
    return typeof first?.value === 'string' ? first.value.trim() : '';

View on GitHub (pinned to 49907e53dc)