can1357/oh-my-pi · error · ToolError

tab.extract(${JSON.stringify(format)}) produced empty ${form

Error message

tab.extract(${JSON.stringify(format)}) produced empty ${format} content for ${page.url()}

What it means

Second-stage validation in `tab.extract`: readability succeeded but the requested format's output (`result.markdown` or `result.text`) is empty/whitespace, so this ToolError is thrown. It covers pages where a readable body exists structurally but contains no textual content in the requested format.

Source

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

						}
					},
				),
			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);
							try {
								await untilAborted(sig, () => handle.click());
							} finally {
								await handle.dispose().catch(() => undefined);
							}
							return;

View on GitHub (pinned to 9690622007)

Solutions

  1. Try the other format (e.g. 'markdown' instead of 'text') — one projection may retain content the other drops
  2. Fall back to ariaSnapshot to capture the accessibility tree text instead of readability extraction
  3. Extract from a specific content element rather than the whole page
  4. If the content is genuinely non-textual, use a screenshot/visual inspection instead of extract

Example fix

// before
const text = await tab.extract('text'); // empty on gallery page
// after
let content = await tab.extract('text');
if (!content) content = await tab.extract('markdown');
if (!content) content = await tab.ariaSnapshot();
Defensive patterns

Strategy: fallback

Validate before calling

const html = await tab.content();
const textOnly = html.replace(/<[^>]+>/g, '').trim();
if (!textOnly) throw new Error('page has no textual content — extract will be empty');

Try / catch

try {
  return await tab.extract(format);
} catch (err) {
  if (err instanceof ToolError && err.message.includes('produced empty')) {
    for (const f of alternateFormats) {
      const c = await tab.extract(f).catch(() => null);
      if (c) return c;
    }
    return await tab.ariaSnapshot();
  }
  throw err;
}

Prevention

When it happens

Trigger: Requesting a format whose projected content is empty: readability found a container but only images/embeds/no text (e.g. gallery or video page), or the text projection stripped everything.

Common situations: Image/video-dominated pages (Instagram-style galleries); pages where content is canvas/canvas-rendered; extracting 'text' from a page whose body is only links/menus; extraction of a mostly-iframed page.

Related errors


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