jackwener/OpenCLI · error · CommandExecutionError
${label} failed: ${error?.message ?? error}
Error message
${label} failed: ${error?.message ?? error} What it means
runBrowserTask wraps an async browser task and re-throws typed errors (whose code is in TYPED_ERROR_CODES) untouched, but converts any other thrown value into a CommandExecutionError labeled with the task name. This guarantees every untyped failure (network blip, page.goto rejection, arbitrary exception) is reported as '<task> failed: <reason>'.
Source
Thrown at clis/dribbble/utils.js:106
if (!payload || typeof payload !== 'object') {
throw new CommandExecutionError(`${command} returned an unreadable browser payload`);
}
if (payload.empty) {
throw new EmptyResultError(command, payload.reason || `${command} was not found`);
}
if (!payload.ok || !payload.row) {
const reason = payload.reason ? `: ${payload.reason}` : '';
throw new CommandExecutionError(`${command} selector drift${reason}`);
}
return payload.row;
}
export async function runBrowserTask(label, task) {
try {
return await task();
} catch (error) {
if (TYPED_ERROR_CODES.has(error?.code)) throw error;
throw new CommandExecutionError(`${label} failed: ${error?.message ?? error}`);
}
}
export function extractShotRows(limit) {
const clean = (value) => String(value ?? '').replace(/\s+/g, ' ').trim();
const count = (value) => {
const text = clean(value).replace(/,/g, '');
const match = text.match(/^(\d+(?:\.\d+)?)\s*([kmb])?$/i);
if (!match) return null;
const multiplier = { k: 1e3, m: 1e6, b: 1e9 }[String(match[2] ?? '').toLowerCase()] ?? 1;
return Number(match[1]) * multiplier;
};
const root = document.querySelector('#content, main, [role="main"]');
const cards = [...document.querySelectorAll('li[id^="screenshot-"]')];
if (/whoops, that page is gone/i.test(document.body?.textContent || '')) {
return { ok: true, empty: true, reason: 'Dribbble profile or shot page was not found' };
}
if (!root) {View on GitHub (pinned to 49907e53dc)
Solutions
- Read error.message after the label to find the underlying cause
- Retry the command — transient network/navigation failures are the most common cause
- Check network/DNS/proxy connectivity to dribbble.com
- If the cause is a code bug, fix it or wrap it in a typed error so it propagates unchanged
- Catch CommandExecutionError at the CLI boundary and print the message to the user
Example fix
// before
const data = await runBrowserTask('dribbble shots', task); // throws on any failure
// after
try {
const data = await runBrowserTask('dribbble shots', task);
} catch (err) {
if (err.code === 'COMMAND_EXEC' && isTransient(err.message)) return retry(task);
throw err;
} Defensive patterns
Strategy: try-catch
Validate before calling
// ensure connectivity before the browser task
const res = await fetch('https://dribbble.com', { method: 'HEAD' }).catch(() => null);
if (!res) throw new Error('dribbble.com unreachable'); Type guard
function isTypedError(e) {
return ['ARGUMENT','AUTH_REQUIRED','COMMAND_EXEC','EMPTY_RESULT','TIMEOUT'].includes(e?.code);
} Try / catch
try {
return await runBrowserTask(label, task);
} catch (err) {
if (isTypedError(err)) throw err; // typed: propagate
// untyped (wrapped as '<label> failed: ...')
if (isTransient(err.message)) return runBrowserTask(label, task); // retry once
throw err;
} Prevention
- Throw typed errors inside tasks so they bypass wrapping
- Add retry with backoff around runBrowserTask for transient failures
- Check network/proxy health in the environment before scraping
- Log err.message (the embedded cause) for root-cause analysis
When it happens
Trigger: Any exception inside the browser task that lacks a code in {ARGUMENT, AUTH_REQUIRED, COMMAND_EXEC, EMPTY_RESULT, TIMEOUT} — e.g. page.goto network error, navigation timeout with non-typed code, a TypeError in extraction code, or a thrown non-Error value.
Common situations: Dribbble unreachable or DNS failure; browser crashed mid-task; a bug in the extraction function throwing TypeError; proxy/network outage in the environment.
Related errors
- Browser session required for twitter mute-word
- Not a git repository
- Working tree not clean: ${status}
- Stale .git/index.lock found — remove it first
- 12306 whoami failed: ${probe.detail}
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/da22cd7a6914f6c3.
Report an issue: GitHub.