{"record":{"id":"4e62188f9d832aa2","repo":"jackwener/OpenCLI","slug":"lobsters-api-http-res-status-for-story-shorti","errorCode":null,"errorMessage":"Lobsters API HTTP ${res.status} for story ${shortId}","messagePattern":"Lobsters API HTTP (.+?) for story (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/lobsters/read.js","lineNumber":26,"sourceCode":" * just need one HTTP call, then build a children map and DFS.\n *\n * Output rows:\n *   - first row is the story itself (`type=POST`)\n *   - each subsequent row is a comment, indented by depth (`L0`, `L1`, …)\n *   - `[+N more replies]` summary rows whenever depth/limit cuts in\n */\nimport { cli, Strategy } from '@jackwener/opencli/registry';\nimport { ArgumentError, CommandExecutionError, EmptyResultError } from '@jackwener/opencli/errors';\n\nconst LOBSTERS_STORY_BASE = 'https://lobste.rs/s';\n\nasync function fetchStory(shortId) {\n    const res = await fetch(`${LOBSTERS_STORY_BASE}/${shortId}.json`);\n    if (res.status === 404) {\n        throw new EmptyResultError(`lobsters/${shortId}`, 'Story not found');\n    }\n    if (!res.ok) {\n        throw new CommandExecutionError(`Lobsters API HTTP ${res.status} for story ${shortId}`, 'Check the short id');\n    }\n    return res.json();\n}\n\nfunction requirePositiveInt(value, label) {\n    if (!Number.isInteger(value) || value <= 0) {\n        throw new ArgumentError(`${label} must be a positive integer`);\n    }\n    return value;\n}\n\nfunction requireMinInt(value, min, label) {\n    if (!Number.isInteger(value) || value < min) {\n        throw new ArgumentError(`${label} must be an integer >= ${min}`);\n    }\n    return value;\n}\n","sourceCodeStart":8,"sourceCodeEnd":44,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/lobsters/read.js#L8-L44","documentation":"Thrown by fetchStory in clis/lobsters/read.js:26 when the lobste.rs JSON endpoint returns a non-OK status other than 404. 404 is converted to EmptyResultError; anything else (5xx, 403 rate-limited, network-edge errors surfaced as status) becomes this generic CommandExecutionError wrapping the HTTP status and the story short id. It signals the request reached the Lobsters API but the API rejected or failed to serve it.","triggerScenarios":"Calling `lobsters read <short_id>` where the GET https://lobste.rs/s/<short_id>.json returns e.g. 500/502/503 (Lobsters outage or Cloudflare issue) or 403/429 (rate limiting, blocked IP). Only statuses other than 404 produce this.","commonSituations":"Lobsters is down or under maintenance; a corporate proxy/CDN blocks lobste.rs; short-id typos that accidentally hit an edge route returning 403/410 instead of 404; aggressive scripted polling triggering 429 rate limits.","solutions":["Check https://lobste.rs is reachable in a browser and inspect the actual HTTP status from the message","Retry after a delay if the status is 429/5xx — Lobsters rate-limits and has outages","Verify the short id is a valid lowercase-alphanumeric id (regex ^[a-z0-9]+$ passes before fetch)","Check for proxy/VPN/firewall interference with lobste.rs requests","If persistent, capture res body/status and report to Lobsters or the CLI maintainers"],"exampleFix":"// before\nconst story = await fetchStory(shortId);\n// after\ntry {\n  const story = await fetchStory(shortId);\n} catch (e) {\n  if (/HTTP 429|HTTP 5\\d\\d/.test(e.message)) {\n    await new Promise(r => setTimeout(r, 5000));\n    return fetchStoryWithRetry(shortId);\n  }\n  throw e;\n}","handlingStrategy":"retry","validationCode":"const shortId = String(id || '').trim();\nif (!/^[a-z0-9]+$/.test(shortId)) throw new Error(`Invalid short id: ${id}`);","typeGuard":"function isValidShortId(id) {\n  return typeof id === 'string' && /^[a-z0-9]+$/.test(id.trim());\n}","tryCatchPattern":"try {\n  const story = await fetchStory(id);\n} catch (e) {\n  if (/HTTP (429|5\\d\\d)/.test(e.message)) {\n    await sleep(5000);\n    return fetchStory(id); // bounded retries\n  }\n  throw e;\n}","preventionTips":["Validate the short id format before calling","Back off and retry on 429/5xx only","Poll Lobsters status before bulk runs","Avoid high-frequency scripted reads that trip rate limits"],"tags":["network","http","api","rate-limit"],"backgroundTag":"http-5xx-api-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}