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.
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
- BUTTON_NOT_FOUND: Follow button not on profile page (logged…
- 频道 返回空列表。
- page loaded but no matching rows were found
- returned no results
- Could not find chart data on page
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/1f220b3aa4afac88.
Report an issue: GitHub.
Appendix: 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)