jackwener/OpenCLI · error · CommandExecutionError
dianping ${label} failed: ${message}
Error message
dianping ${label} failed: ${message} What it means
wrapDianpingStep wraps a dianping step's async function and rethrows unexpected failures as CommandExecutionError with a 'dianping <label> failed: ...' prefix. Typed errors (any err with a .code — e.g. AuthRequiredError, EmptyResultError, ArgumentError) are passed through untouched; only code-less errors (TypeError, network crashes, selector misses) get wrapped. This normalizes raw exceptions from browser automation into a labeled, grep-able CLI error.
Source
Thrown at clis/dianping/utils.js:94
export function normalizeShopId(rawInput) {
const raw = String(rawInput || '').trim();
if (!raw) throw new ArgumentError('shop_id must be a non-empty string');
const idMatch = raw.match(/\/shop\/([^?#/]+)/);
const shopId = idMatch ? idMatch[1] : raw;
if (!/^[A-Za-z0-9_-]+$/.test(shopId)) {
throw new ArgumentError(`'${raw}' does not look like a dianping shop id`);
}
return shopId;
}
export function wrapDianpingStep(label, fn) {
return Promise.resolve()
.then(fn)
.catch((err) => {
if (err?.code) throw err;
const message = err?.message || String(err);
throw new CommandExecutionError(`dianping ${label} failed: ${message}`);
});
}
/**
* Throw the right typed error for a dianping page that didn't render data.
* The site short-circuits HTML when bot/login checks trip — typically
* redirects to verify.meituan.com (Yoda icon-tap captcha) or to a login
* page when the cookie is missing.
*/
export function detectAuthOrPageFailure({ text = '', url = '' }, contextHint, { emptyPatterns = [] } = {}) {
const signal = `${url} ${text}`;
if (/verify\.meituan\.com|verifyimg|身份核实|请依次点击|美团安全验证|Yoda/i.test(signal)) {
throw new AuthRequiredError(
'dianping.com',
`dianping ${contextHint} blocked by captcha — open ${url || 'www.dianping.com'} manually in this profile and solve the captcha, then retry`,
);
}
if (/login\.dianping\.com|account\.dianping\.com|请先登录|未登录|请登录/.test(signal)) {View on GitHub (pinned to 49907e53dc)
Solutions
- Read the wrapped inner message after 'failed: ' to find the original cause (TypeError, timeout, etc.).
- Check that the browser profile/page is alive and not crashing; rerun with the browser visible to see where navigation stops.
- If it is a code bug throwing a non-Error value, fix the throw site to throw a typed error from @jackwener/opencli/errors so wrapDianpingStep passes it through.
- Retry the step — transient navigation/anti-bot hiccups often resolve on a second run; persistent captcha will surface as a typed AuthRequiredError instead.
Example fix
// before
throw 'selector not found'; // wrapped as 'dianping result failed: selector not found'
// after
import { CommandExecutionError } from '@jackwener/opencli/errors';
throw new CommandExecutionError('selector not found'); // passes through with .code Defensive patterns
Strategy: try-catch
Type guard
function isTypedError(err) {
return err instanceof Error && typeof err.code === 'string';
} Try / catch
try {
await runDianpingStep();
} catch (err) {
if (err.code) {
// typed: AuthRequiredError / EmptyResultError / ArgumentError — handle specifically
} else {
// wrapped CommandExecutionError: inspect err.message after 'failed: ' for the raw cause
}
} Prevention
- Always throw Error instances with .code from adapter code so wrapDianpingStep passes them through instead of wrapping.
- Keep the browser page alive for the whole step; guard against navigation timeouts.
- Log err.stack of the wrapped error to locate the true throw site.
When it happens
Trigger: Any step registered via wrapDianpingStep (cityId, result, data steps) throwing or rejecting with an error that has no .code property — e.g. a TypeError inside a page.evaluate callback, a browser/page crash, a timed-out or closed Playwright/Puppeteer target, or a plain string/undefined throw.
Common situations: Playwright browser closed mid-navigation, page.goto timeout, a selector helper throwing undefined because a DOM node disappeared during re-render, or a bug in adapter code throwing a literal value instead of a typed error.
Related errors
- xiaohongshu/follow failed: ${err?.message ?? String(err)}
- xiaohongshu/unfollow failed: ${err?.message ?? String(err)}
- Unexpected 12306 probe: ${JSON.stringify(probe)}
- Waiting for 12306 tk auth cookie
- ${label} failed: ${error?.message ?? error}
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/1478eb20e7587c5a.
Report an issue: GitHub.