jackwener/OpenCLI · error · CommandExecutionError

lobsters domain returned HTTP ${resp.status}

Error message

lobsters domain returned HTTP ${resp.status}

What it means

This CommandExecutionError is thrown when the lobste.rs domain endpoint returns any non-success HTTP status other than 404 (which is handled separately as an empty result). It signals the request reached lobste.rs but the server rejected or failed it. The message embeds the raw status code so the developer can tell rate limiting (429), auth/permission (403), or server-side errors (5xx) apart.

Source

Thrown at clis/lobsters/domain.js:66

    func: async (args) => {
        const domain = requireDomain(args.domain);
        const limit = requireBoundedInt(args.limit, 20, 25);
        const url = `https://lobste.rs/domains/${encodeURIComponent(domain)}.json`;
        let resp;
        try {
            resp = await fetch(url, { headers: { 'user-agent': 'opencli-lobsters-adapter (+https://github.com/jackwener/opencli)' } });
        }
        catch (err) {
            throw new CommandExecutionError(
                `lobsters domain request failed: ${err?.message ?? err}`,
                'Check that lobste.rs is reachable from this network.',
            );
        }
        if (resp.status === 404) {
            throw new EmptyResultError('lobsters domain', `No Lobste.rs stories found for domain "${domain}".`);
        }
        if (!resp.ok) {
            throw new CommandExecutionError(`lobsters domain returned HTTP ${resp.status}`);
        }
        let body;
        try {
            body = await resp.json();
        }
        catch (err) {
            throw new CommandExecutionError(`lobsters domain returned malformed JSON: ${err?.message ?? err}`);
        }
        const list = Array.isArray(body) ? body : [];
        if (!list.length) {
            throw new EmptyResultError('lobsters domain', `No Lobste.rs stories found for domain "${domain}".`);
        }
        return list.slice(0, limit).map((item, i) => ({
            rank: i + 1,
            id: String(item.short_id ?? ''),
            title: String(item.title ?? ''),
            score: item.score != null ? Number(item.score) : null,
            author: String(item.submitter_user ?? ''),

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Read the HTTP status in the message and check lobste.rs availability/status page if it is 5xx.
  2. If 429, back off and retry later; add caching or reduce polling frequency of the lobste.rs API.
  3. If 403, retry from a different network/IP or check whether a proxy/firewall is blocking lobste.rs.
  4. Wrap the call with an exponential-backoff retry for transient 5xx statuses.

Example fix

// before
const stories = await cli.lobsters.domain('example.com'); // throws on 503

// after
async function withRetry(fn, attempts = 3) {
  for (let i = 0; i < attempts; i++) {
    try { return await fn(); }
    catch (e) {
      if (i === attempts - 1 || !/HTTP 5\d\d/.test(String(e.message))) throw e;
      await new Promise(r => setTimeout(r, 2 ** i * 500));
    }
  }
}
const stories = await withRetry(() => cli.lobsters.domain('example.com'));
Defensive patterns

Strategy: retry

Validate before calling

async function lobstersReachable() {
  try {
    const r = await fetch('https://lobste.rs/', { method: 'HEAD' });
    return r.status < 500;
  } catch {
    return false;
  }
}
if (!(await lobstersReachable())) {
  console.error('lobste.rs is unreachable or returning server errors; aborting.');
  return;
}

Type guard

null

Try / catch

try {
  const stories = await cli.lobsters.domain(domain);
} catch (err) {
  const m = /HTTP (\d{3})/.exec(String(err.message));
  if (m) {
    const status = Number(m[1]);
    if (status === 429) console.error('Rate limited by lobste.rs — wait before retrying.');
    else if (status >= 500) console.error('lobste.rs server error — retry with backoff.');
    else console.error(`lobste.rs rejected the request (HTTP ${status}).`);
    return;
  }
  throw err;
}

Prevention

When it happens

Trigger: GET https://lobste.rs/domain/<domain>.json returns status 403, 429, 500, 502, 503, etc. — anything where resp.ok is false and resp.status !== 404.

Common situations: Hitting lobste.rs rate limits after scripted polling (HTTP 429), Cloudflare or proxy blocking the request (403), lobste.rs maintenance or partial outages (502/503), or a corporate proxy injecting error pages with 4xx/5xx codes.

Related errors


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