{"record":{"id":"5a540b64b4f49d5d","repo":"jackwener/OpenCLI","slug":"browser-session-required-for-substack-archive","errorCode":null,"errorMessage":"Browser session required for substack archive","messagePattern":"Browser session required for substack archive","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/substack/utils.js","lineNumber":80,"sourceCode":"          title,\n          author,\n          date,\n          readTime,\n          description: description.slice(0, 150),\n          url: postUrl,\n        });\n\n        if (posts.length >= limit) break;\n      }\n\n      return posts;\n    })()\n  `);\n    return Array.isArray(data) ? data : [];\n}\nexport async function loadSubstackArchive(page, baseUrl, limit) {\n    if (!page)\n        throw new CommandExecutionError('Browser session required for substack archive');\n    await page.goto(`${baseUrl}/archive`);\n    await page.wait({ selector: ARCHIVE_POST_LINK_SELECTOR, timeout: 5 });\n    const data = await page.evaluate(`\n    (async () => {\n      await new Promise((resolve) => setTimeout(resolve, 3000));\n      const normalize = (value) => (value || '').replace(/\\\\s+/g, ' ').trim();\n      const limit = ${Math.max(1, Math.min(limit, 50))};\n      const grouped = new Map();\n\n      for (const link of Array.from(document.querySelectorAll('a[href*=\"/p/\"]'))) {\n        const rawHref = link.getAttribute('href') || '';\n        if (!rawHref || rawHref === '/p/upgrade') continue;\n\n        const url = rawHref.startsWith('http') ? rawHref : ${JSON.stringify(baseUrl)} + rawHref;\n        const text = normalize(link.textContent);\n        if (!text) continue;\n        if (/^(subscribe|paid|home|about|latest|top|discussions)$/i.test(text)) continue;\n        if (/^[\\\\d,]+$/.test(text)) continue;","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/substack/utils.js#L62-L98","documentation":"loadSubstackArchive requires an active browser session to scrape a publication's /archive page, which is rendered client-side. Without a page it throws a CommandExecutionError before any navigation happens.","triggerScenarios":"Calling loadSubstackArchive with page = null/undefined, e.g. invoking the archive command without a browser session, or automation that never launched a browser.","commonSituations":"Batch archive scraping scripts run headlessly without session setup, browser launch failures treated as non-fatal earlier in the pipeline, CI environments lacking a browser binary.","solutions":["Open a browser session before calling loadSubstackArchive","Add an explicit session-creation step and assert the page object exists","Install/verify the browser runtime in CI or container environments","Use the publication's RSS feed as a fallback for simple archive listings"],"exampleFix":"// before\nawait loadSubstackArchive(null, 'https://example.substack.com', 20); // throws\n// after\nif (!page) page = await openBrowserSession();\nawait loadSubstackArchive(page, 'https://example.substack.com', 20);","handlingStrategy":"validation","validationCode":"function requirePage(page) { if (!page || typeof page.goto !== 'function') throw new Error('open a browser session before loading the substack archive'); }\nrequirePage(page);","typeGuard":"function hasPage(p) { return p != null && typeof p.goto === 'function' && typeof p.evaluate === 'function'; }","tryCatchPattern":"try { await loadSubstackArchive(page, baseUrl, limit); } catch (e) { if (/Browser session required/.test(e.message)) { page = await openBrowserSession(); await loadSubstackArchive(page, baseUrl, limit); } else throw e; }","preventionTips":["Launch the browser session before archive scraping","Check session state at script start","Install browsers in container/CI images","Use RSS archives where a browser is unavailable"],"tags":["browser-session","substack","precondition"],"backgroundTag":"browser-session-required","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}