jackwener/OpenCLI · error · CommandExecutionError

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

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.

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)

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.