jackwener/OpenCLI · error · CliError
NO_DATA
NO_DATA
Error message
Could not retrieve Product Hunt top posts
What it means
CliError('NO_DATA') thrown by clis/producthunt/hot.js:91 when the DOM scraping script inside page.evaluate finds zero product cards on the Product Hunt homepage. The command relies on an INTERCEPT strategy plus DOM scraping of a[href^="/products/"] links, so any change to Product Hunt's markup (or a failed/blocked page load) yields an empty items array and this error. It signals that the CLI could not extract top posts, not that the network call itself failed.
Source
Thrown at clis/producthunt/hot.js:91
})
.filter((candidate) => /^\\d+$/.test(candidate.text));
if (voteCandidates.length === 0) continue;
seen.add(href);
results.push({
name,
voteCandidates,
url: 'https://www.producthunt.com' + href,
});
}
return results;
})()
`);
const items = Array.isArray(domItems) ? domItems : [];
if (items.length === 0) {
throw new CliError('NO_DATA', 'Could not retrieve Product Hunt top posts', 'Product Hunt may have changed its layout');
}
const rankedItems = items
.map((item) => ({
name: item.name,
url: item.url,
votes: pickVoteCount(Array.isArray(item.voteCandidates) ? item.voteCandidates : []),
}))
.filter((item) => item.name && item.url && item.votes);
if (rankedItems.length === 0) {
throw new CliError('NO_DATA', 'Could not retrieve Product Hunt vote counts', 'Product Hunt may have changed its vote button structure');
}
rankedItems.sort((a, b) => parseInt(b.votes, 10) - parseInt(a.votes, 10));
return rankedItems.slice(0, count).map((item, i) => ({
rank: i + 1,
name: item.name,
votes: item.votes,
url: item.url,
}));View on GitHub (pinned to 49907e53dc)
Solutions
- Open https://www.producthunt.com in a browser and confirm a[href^="/products/"] cards still exist; update selectors if the layout changed
- Re-run with a longer wait (increase waitForCapture timeout) to rule out slow rendering
- Check whether Product Hunt is serving a captcha/consent page to your IP; use a different network or add consent-cookie handling
- Verify installInterceptor is capturing the expected requests; inspect captured payloads for the feed data
- Retry later — transient bot-blocking or partial outage can produce an empty DOM
Example fix
// before
const items = Array.isArray(domItems) ? domItems : [];
if (items.length === 0) {
throw new CliError('NO_DATA', 'Could not retrieve Product Hunt top posts', 'Product Hunt may have changed its layout');
}
// after
const items = Array.isArray(domItems) ? domItems : [];
if (items.length === 0) {
// give the SPA more time, then retry once before failing
await page.waitForTimeout(3000);
const retryItems = Array.isArray(await page.evaluate(SCRAPE_SNIPPET)) ? domItems : [];
if (retryItems.length === 0) {
throw new CliError('NO_DATA', 'Could not retrieve Product Hunt top posts', 'Product Hunt may have changed its layout');
}
} Defensive patterns
Strategy: try-catch
Validate before calling
// preflight: confirm the page renders product cards before running the command
const cardCount = await page.evaluate(`document.querySelectorAll('a[href^="/products/"]').length`);
if (!cardCount) throw new Error('Product Hunt page rendered no product cards — likely blocked or redesigned'); Type guard
function isDomItemArray(v) {
return Array.isArray(v) && v.every((i) => i && typeof i.name === 'string' && typeof i.url === 'string');
} Try / catch
try {
const posts = await runCli('producthunt hot', { limit: 20 });
} catch (e) {
if (e?.code === 'NO_DATA') {
console.warn('Product Hunt layout may have changed; retrying with longer wait or falling back to cached data.');
return cachedPosts ?? [];
}
throw e;
} Prevention
- Pin/update selectors after each Product Hunt redesign; add a smoke test asserting >0 cards
- Increase waitForCapture timeout for slow/headless environments
- Handle consent walls and bot challenges before scraping
- Fall back to intercepted network payloads when DOM scraping returns empty
When it happens
Trigger: page.evaluate returns [] or non-array: (a) page.goto loaded a captcha/login/blocked page instead of the homepage, (b) Product Hunt renamed or removed the a[href^="/products/"] card structure or added /reviews filter that excludes all links, (c) waitForCapture(5) expired before cards rendered, (d) the interceptor captured nothing and the SPA never rendered product cards.
Common situations: Product Hunt ships a redesign or A/B test changing product-card markup; running in a region/IP that gets a bot-challenge page; slow network or headless environment where hydration hasn't finished in 5 seconds; scraping from CI where the homepage serves a consent wall.
Related errors
- Not a git repository
- Working tree not clean: ${status}
- NO_DATA
- NO_DATA
- Failed to extract Booking.com cards: ${err?.message || err}
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/1f220b3aa4afac88.
Report an issue: GitHub.