{"record":{"id":"edb52858326174d1","repo":"jackwener/OpenCLI","slug":"no-lobste-rs-stories-found-for-domain-domain","errorCode":null,"errorMessage":"No Lobste.rs stories found for domain \"${domain}\".","messagePattern":"No Lobste\\.rs stories found for domain \"(.+?)\"\\.","errorType":"exception","errorClass":"EmptyResultError","httpStatus":404,"severity":"warning","filePath":"clis/lobsters/domain.js","lineNumber":63,"sourceCode":"        { name: 'limit', type: 'int', default: 20, help: 'Number of stories (1-25 — single page)' },\n    ],\n    columns: ['rank', 'id', 'title', 'score', 'author', 'comments', 'created_at', 'tags', 'submission_url', 'comments_url'],\n    func: async (args) => {\n        const domain = requireDomain(args.domain);\n        const limit = requireBoundedInt(args.limit, 20, 25);\n        const url = `https://lobste.rs/domains/${encodeURIComponent(domain)}.json`;\n        let resp;\n        try {\n            resp = await fetch(url, { headers: { 'user-agent': 'opencli-lobsters-adapter (+https://github.com/jackwener/opencli)' } });\n        }\n        catch (err) {\n            throw new CommandExecutionError(\n                `lobsters domain request failed: ${err?.message ?? err}`,\n                'Check that lobste.rs is reachable from this network.',\n            );\n        }\n        if (resp.status === 404) {\n            throw new EmptyResultError('lobsters domain', `No Lobste.rs stories found for domain \"${domain}\".`);\n        }\n        if (!resp.ok) {\n            throw new CommandExecutionError(`lobsters domain returned HTTP ${resp.status}`);\n        }\n        let body;\n        try {\n            body = await resp.json();\n        }\n        catch (err) {\n            throw new CommandExecutionError(`lobsters domain returned malformed JSON: ${err?.message ?? err}`);\n        }\n        const list = Array.isArray(body) ? body : [];\n        if (!list.length) {\n            throw new EmptyResultError('lobsters domain', `No Lobste.rs stories found for domain \"${domain}\".`);\n        }\n        return list.slice(0, limit).map((item, i) => ({\n            rank: i + 1,\n            id: String(item.short_id ?? ''),","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/lobsters/domain.js#L45-L81","documentation":"This EmptyResultError is thrown by the `lobsters domain` command when the lobste.rs domain endpoint responds with HTTP 404, meaning no stories exist for the requested domain on Lobste.rs. The library treats 'no matching stories' as an expected empty result rather than a failure, so it uses EmptyResultError instead of CommandExecutionError. It lets callers distinguish 'bad input/domain has no coverage' from network or server problems.","triggerScenarios":"Running `lobsters domain <domain>` where GET https://lobste.rs/domain/<domain>.json returns status 404 — i.e. the domain has never had a story submitted to Lobste.rs, or the domain string is malformed so it maps to a nonexistent route.","commonSituations":"Typo or bad normalization of the domain (e.g. passing 'https://example.com' or 'example.com/path' instead of 'example.com'), querying an obscure internal or very new domain that no Lobste.rs user has posted, or a case/punctuation mismatch (trailing slash, uppercase).","solutions":["Normalize the domain before calling: strip scheme (https://), path, and trailing slash, and lowercase it (new URL(domain).hostname).","Verify the domain actually has stories by opening https://lobste.rs/domain/<domain> in a browser.","If the domain is valid but has no coverage, treat this as an expected empty result: catch EmptyResultError and render a friendly 'no stories' message instead of an error.","Check the lobste.rs URL construction in clis/lobsters/domain.js if many valid domains unexpectedly 404 (possible API route change)."],"exampleFix":"// before\nawait cli.lobsters.domain('https://example.com/');\n// throws EmptyResultError: No Lobste.rs stories found for domain\n\n// after\nconst hostname = new URL('https://example.com/').hostname; // 'example.com'\nawait cli.lobsters.domain(hostname);","handlingStrategy":"fallback","validationCode":"function normalizeDomain(input) {\n  try {\n    const { hostname } = new URL(input.includes('://') ? input : `https://${input}`);\n    return hostname.toLowerCase();\n  } catch {\n    return null; // invalid input, don't call the API\n  }\n}\nconst domain = normalizeDomain(userInput);\nif (!domain) throw new Error(`Invalid domain: ${userInput}`);","typeGuard":"function isValidDomain(s) {\n  return typeof s === 'string' && /^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)+$/i.test(s);\n}","tryCatchPattern":"try {\n  const stories = await cli.lobsters.domain(domain);\n} catch (err) {\n  if (err.name === 'EmptyResultError') {\n    console.log(`No Lobste.rs stories for ${domain} — this is expected for niche domains.`);\n    return [];\n  }\n  throw err;\n}","preventionTips":["Always normalize user-supplied domains with new URL(...).hostname before passing them in.","Reject inputs containing schemes, paths, or query strings at the CLI argument-parsing layer.","Default to a friendly 'no stories found' UX when EmptyResultError is caught.","Sanity-check unfamiliar domains against https://lobste.rs/domain/<domain> manually once."],"tags":["empty-result","http-404","cli","input-validation"],"backgroundTag":"empty-result-no-matches","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}