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

  1. Read error.message after the label to find the underlying cause
  2. Retry the command — transient network/navigation failures are the most common cause
  3. Check network/DNS/proxy connectivity to dribbble.com
  4. If the cause is a code bug, fix it or wrap it in a typed error so it propagates unchanged
  5. 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

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


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/da22cd7a6914f6c3. Report an issue: GitHub.