{"record":{"id":"282f77ef31b5c553","repo":"jackwener/OpenCLI","slug":"unexpected-smzdm-search-extraction-payload-shape","errorCode":null,"errorMessage":"Unexpected SMZDM search extraction payload shape; expected an array of rows.","messagePattern":"Unexpected SMZDM search extraction payload shape; expected an array of rows\\.","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/smzdm/search.js","lineNumber":21,"sourceCode":" *\n * Fix: The old adapter used `search.smzdm.com/ajax/` which returns 404.\n * New approach: navigate to `search.smzdm.com/?c=home&s=<keyword>&v=b`\n * and scrape the rendered DOM directly.\n */\nimport { cli, Strategy } from '@jackwener/opencli/registry';\nimport { ArgumentError, CommandExecutionError } from '@jackwener/opencli/errors';\n\nfunction unwrapEvaluateResult(payload) {\n    if (payload && !Array.isArray(payload) && typeof payload === 'object' && 'session' in payload && 'data' in payload) {\n        return payload.data;\n    }\n    return payload;\n}\n\nfunction requireSearchRows(payload) {\n    const rows = unwrapEvaluateResult(payload);\n    if (!Array.isArray(rows)) {\n        throw new CommandExecutionError('Unexpected SMZDM search extraction payload shape; expected an array of rows.');\n    }\n    return rows;\n}\n\nfunction parseLimit(raw) {\n    let parsed;\n    if (raw == null) {\n        parsed = 20;\n    }\n    else if (typeof raw === 'number') {\n        parsed = raw;\n    }\n    else if (typeof raw === 'string' && /^[0-9]+$/.test(raw)) {\n        parsed = Number(raw);\n    }\n    else {\n        parsed = NaN;\n    }","sourceCodeStart":3,"sourceCodeEnd":39,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/smzdm/search.js#L3-L39","documentation":"requireSearchRows validates that the payload returned from the SMZDM in-page extraction script is an array of result rows after unwrapping an evaluate() result. If the browser-side script returns an object, null, or an error-shaped value instead of an array, the payload shape is considered broken and the CLI refuses to continue rather than crash downstream with a confusing TypeError. This is a defensive contract check on automation output.","triggerScenarios":"smzdmSearchCommand runs the extraction script via unwrapEvaluateResult(payload) and the result is not an Array — e.g. the page DOM changed so the script returns an error object, the browser evaluate call failed and returned {error: ...} or null, or a rate-limit/anti-bot page was served instead of results.","commonSituations":"SMZDM changes its search page markup so the injected selector-based script returns {} or an error payload; anti-bot interception returns a challenge page; the automation driver wraps results differently after a driver upgrade; the site returns a login wall so the script bails with a non-array value.","solutions":["Re-run the command to rule out a transient anti-bot or rate-limit page interception.","Inspect the raw payload (log unwrapEvaluateResult(payload)) to see what shape actually came back.","Update the SMZDM extraction script selectors to match the current search page markup.","Check the automation/browser driver version for evaluate() result-serialization changes."],"exampleFix":"// before: assume array, crash with TypeError on .map\nconst rows = unwrapEvaluateResult(payload);\nreturn rows.map(formatRow);\n// after: validate first (what requireSearchRows does)\nconst rows = unwrapEvaluateResult(payload);\nif (!Array.isArray(rows)) throw new CommandExecutionError('Unexpected SMZDM search extraction payload shape; expected an array of rows.');","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"function isRows(payload) { return Array.isArray(unwrapEvaluateResult(payload)); }\nif (!isRows(payload)) { /* bail out or log payload before calling the command */ }","tryCatchPattern":"try {\n  const rows = requireSearchRows(payload);\n} catch (e) {\n  console.error('SMZDM extraction returned non-array; raw payload:', JSON.stringify(unwrapEvaluateResult(payload)));\n  // fall back to retry or surface a user-facing 'site layout may have changed' message\n}","preventionTips":["Log the raw payload before validation so shape regressions are diagnosable.","Pin/alert on SMZDM page markup changes; smoke-test the extraction script in CI.","Handle anti-bot/rate-limit interception explicitly before extraction."],"tags":["cli","payload-shape","web-scraping","validation"],"backgroundTag":"unexpected-payload-shape","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}