{"record":{"id":"9137554d7cd7092e","repo":"jackwener/OpenCLI","slug":"label-returned-an-unexpected-payload-shape-exp-913755","errorCode":null,"errorMessage":"${label} returned an unexpected payload shape; expected an array of result rows.","messagePattern":"(.+?) returned an unexpected payload shape; expected an array of result rows\\.","errorType":"validation","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/_shared/search-adapter.js","lineNumber":42,"sourceCode":"  const raw = value ?? defaultValue;\n  const parsed = typeof raw === 'number' ? raw : Number(raw);\n  if (!Number.isInteger(parsed) || parsed < 0) {\n    throw new ArgumentError(`${label} must be a non-negative integer, got ${JSON.stringify(value)}`);\n  }\n  return parsed;\n}\n\nexport function unwrapBrowserResult(value) {\n  if (value && typeof value === 'object' && !Array.isArray(value) && 'session' in value && 'data' in value) {\n    return value.data;\n  }\n  return value;\n}\n\nexport function requireRows(value, label) {\n  const rows = unwrapBrowserResult(value);\n  if (!Array.isArray(rows)) {\n    throw new CommandExecutionError(`${label} returned an unexpected payload shape; expected an array of result rows.`);\n  }\n  return rows;\n}\n\nexport function toHttpsUrl(value, baseUrl) {\n  const raw = String(value ?? '').trim();\n  if (!raw) return '';\n  try {\n    const url = new URL(raw, baseUrl);\n    if (url.protocol !== 'http:' && url.protocol !== 'https:') return '';\n    return url.href;\n  } catch {\n    return '';\n  }\n}\n\nexport function emptySearchResults(site, query) {\n  return new EmptyResultError(`${site} search`, `No ${site} results matched \"${query}\".`);","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/_shared/search-adapter.js#L24-L60","documentation":"requireRows unwraps a browser-command result (peeling off a {session, data} envelope via unwrapBrowserResult) and throws a CommandExecutionError if what remains is not an array of result rows. This indicates the browser step returned something structurally different from what the command's formatter expects — usually page-structure drift or a wrapped/failed payload. It's a shape contract, not a content check (an empty array passes).","triggerScenarios":"The page.evaluate/browser step resolves to a non-array: an object without being unwrapped, null/undefined, or a {session,data} envelope whose data is not an array.","commonSituations":"The site changed its markup so the injected extraction script returns an error object instead of rows; an intermediate wrapper/adapter changed its return shape after a version upgrade; a proxy or login wall returned an HTML page parsed into an object.","solutions":["Log/print the raw browser payload before requireRows to inspect the actual shape.","If a {session,data} envelope is expected, confirm unwrapBrowserResult is being applied and that data is the array.","Update the extraction/selector logic to return an array, or update the command for the site's new DOM.","Pin or upgrade the CLI/adapter versions so payload shape and parser agree."],"exampleFix":"// before (payload shape drifted)\nconst rows = await runBrowserStep('search', () => page.evaluate(js)); // returns {items:[...]}\nrequireRows(rows, 'search');\n// after\nconst payload = await runBrowserStep('search', () => page.evaluate(js));\nrequireRows(Array.isArray(payload) ? payload : payload.items, 'search');","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"function isRows(value) {\n  const v = value && typeof value === 'object' && !Array.isArray(value) && 'session' in value && 'data' in value\n    ? value.data\n    : value;\n  return Array.isArray(v);\n}","tryCatchPattern":"try {\n  const rows = requireRows(payload, 'search');\n} catch (e) {\n  if (e instanceof CommandExecutionError && /unexpected payload shape/.test(e.message)) {\n    console.error('Payload:', JSON.stringify(payload).slice(0, 500));\n  } else throw e;\n}","preventionTips":["Log raw browser payloads when adding new extraction scripts","Assert result shape right after each browser step","Keep adapter/parser versions in sync with payload producers","Treat {session,data} envelopes explicitly before consuming results"],"tags":["cli","payload-shape","browser-scraping"],"backgroundTag":"unexpected-payload-shape","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}