{"record":{"id":"da22cd7a6914f6c3","repo":"jackwener/OpenCLI","slug":"label-failed-error-message-error-da22cd","errorCode":null,"errorMessage":"${label} failed: ${error?.message ?? error}","messagePattern":"(.+?) failed: (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/dribbble/utils.js","lineNumber":106,"sourceCode":"    if (!payload || typeof payload !== 'object') {\n        throw new CommandExecutionError(`${command} returned an unreadable browser payload`);\n    }\n    if (payload.empty) {\n        throw new EmptyResultError(command, payload.reason || `${command} was not found`);\n    }\n    if (!payload.ok || !payload.row) {\n        const reason = payload.reason ? `: ${payload.reason}` : '';\n        throw new CommandExecutionError(`${command} selector drift${reason}`);\n    }\n    return payload.row;\n}\n\nexport async function runBrowserTask(label, task) {\n    try {\n        return await task();\n    } catch (error) {\n        if (TYPED_ERROR_CODES.has(error?.code)) throw error;\n        throw new CommandExecutionError(`${label} failed: ${error?.message ?? error}`);\n    }\n}\n\nexport function extractShotRows(limit) {\n    const clean = (value) => String(value ?? '').replace(/\\s+/g, ' ').trim();\n    const count = (value) => {\n        const text = clean(value).replace(/,/g, '');\n        const match = text.match(/^(\\d+(?:\\.\\d+)?)\\s*([kmb])?$/i);\n        if (!match) return null;\n        const multiplier = { k: 1e3, m: 1e6, b: 1e9 }[String(match[2] ?? '').toLowerCase()] ?? 1;\n        return Number(match[1]) * multiplier;\n    };\n    const root = document.querySelector('#content, main, [role=\"main\"]');\n    const cards = [...document.querySelectorAll('li[id^=\"screenshot-\"]')];\n    if (/whoops, that page is gone/i.test(document.body?.textContent || '')) {\n        return { ok: true, empty: true, reason: 'Dribbble profile or shot page was not found' };\n    }\n    if (!root) {","sourceCodeStart":88,"sourceCodeEnd":124,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/dribbble/utils.js#L88-L124","documentation":"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>'.","triggerScenarios":"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.","commonSituations":"Dribbble unreachable or DNS failure; browser crashed mid-task; a bug in the extraction function throwing TypeError; proxy/network outage in the environment.","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"],"exampleFix":"// before\nconst data = await runBrowserTask('dribbble shots', task); // throws on any failure\n// after\ntry {\n  const data = await runBrowserTask('dribbble shots', task);\n} catch (err) {\n  if (err.code === 'COMMAND_EXEC' && isTransient(err.message)) return retry(task);\n  throw err;\n}","handlingStrategy":"try-catch","validationCode":"// ensure connectivity before the browser task\nconst res = await fetch('https://dribbble.com', { method: 'HEAD' }).catch(() => null);\nif (!res) throw new Error('dribbble.com unreachable');","typeGuard":"function isTypedError(e) {\n  return ['ARGUMENT','AUTH_REQUIRED','COMMAND_EXEC','EMPTY_RESULT','TIMEOUT'].includes(e?.code);\n}","tryCatchPattern":"try {\n  return await runBrowserTask(label, task);\n} catch (err) {\n  if (isTypedError(err)) throw err;               // typed: propagate\n  // untyped (wrapped as '<label> failed: ...')\n  if (isTransient(err.message)) return runBrowserTask(label, task); // retry once\n  throw err;\n}","preventionTips":["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"],"tags":["wrapper","browser-automation","cli"],"backgroundTag":"browser-task-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}