can1357/oh-my-pi · error · ToolError

tab.extract(${JSON.stringify(format)}) found no readable con

Error message

tab.extract(${JSON.stringify(format)}) found no readable content on ${page.url()}

What it means

`tab.extract(format)` fetches page HTML and runs extractReadableFromHtml; when the readability extraction returns null — no article/readable body could be identified — this ToolError is thrown. It signals the page has no extractable main content (not that extraction merely failed to parse).

Source

Thrown at packages/coding-agent/src/tools/browser/tab-worker.ts:1646

								);
						}
						try {
							return await untilAborted(sig, () => captureAriaSnapshot(page, root, opts));
						} finally {
							await root?.dispose().catch(() => undefined);
						}
					},
				),
			screenshot: opts =>
				op(describeScreenshot(opts), quickOpMs, sig =>
					this.#captureScreenshot(session, output, screenshots, sig, opts),
				),
			extract: (format = "markdown") =>
				op(`tab.extract(${JSON.stringify(format)})`, quickOpMs, async sig => {
					const html = (await untilAborted(sig, () => page.content())) as string;
					const result = await extractReadableFromHtml(html, page.url(), format);
					if (!result) {
						throw new ToolError(
							`tab.extract(${JSON.stringify(format)}) found no readable content on ${page.url()}`,
						);
					}
					const content = format === "markdown" ? result.markdown : result.text;
					if (!content) {
						throw new ToolError(
							`tab.extract(${JSON.stringify(format)}) produced empty ${format} content for ${page.url()}`,
						);
					}
					return content;
				}),
			click: selector =>
				op(
					`tab.click(${JSON.stringify(selector)})`,
					actionOpMs,
					async sig => {
						if (parseAriaRefSelector(selector) !== null) {
							const handle = await this.#resolveAriaRef(selector);

View on GitHub (pinned to 9690622007)

Solutions

  1. Wait for the app to render (wait for a content selector) before extracting
  2. Fall back to ariaSnapshot or tab content/screenshot to inspect what the page actually contains
  3. Check the URL — verify it is not an error/interstitial page and that authentication state is established
  4. Use a different format or extract from a specific element if the page has content readability misclassifies

Example fix

// before
const md = await tab.extract('markdown'); // SPA shell, empty body
// after
await tab.wait(() => locationReady && document.querySelector('main article'), { timeout: 10000 });
const md = await tab.extract('markdown');
Defensive patterns

Strategy: fallback

Validate before calling

const html = await tab.content();
if (html.trim().length < 500) throw new Error('page appears empty/unrendered — skip extract');

Try / catch

try {
  return await tab.extract('markdown');
} catch (err) {
  if (err instanceof ToolError && err.message.includes('found no readable content')) {
    return await tab.ariaSnapshot(); // accessibility tree as fallback content
  }
  throw err;
}

Prevention

When it happens

Trigger: Extracting from pages readability cannot model: login-gated shells, pure JS-app skeletons with empty SSR HTML, error pages, cookie/consent walls, PDF/image-only pages, or pages whose main content lives in iframes.

Common situations: Extracting from a SPA before hydration completed; scraping a search results page or dashboard with no article semantics; hitting a paywall/interstitial; extracting from a 404/500 response body.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/0af58bd053dde3e0. Report an issue: GitHub.