Skip to content

fix(pdf): provide standardFontDataUrl for Node fallback rendering#51465

Closed
anyech wants to merge 1238 commits into
openclaw:mainfrom
anyech:fix/pdf-standard-font-data-url
Closed

fix(pdf): provide standardFontDataUrl for Node fallback rendering#51465
anyech wants to merge 1238 commits into
openclaw:mainfrom
anyech:fix/pdf-standard-font-data-url

Conversation

@anyech

@anyech anyech commented Mar 21, 2026

Copy link
Copy Markdown
Contributor

Summary

  • pass standardFontDataUrl into the Node PDF extraction-fallback renderer
  • use the working filesystem-path form (.pathname) instead of file://...href
  • add focused regression coverage for the fallback render path

Closes #51455.

Problem

The non-native PDF extraction fallback could emit repeated PDF.js warnings during page-image rendering in Node:

UnknownErrorException: Ensure that the standardFontDataUrl API parameter is provided.

The analysis still succeeded, but the renderer was missing the standard-font configuration PDF.js expects.

Root cause

The fallback path called getDocument({ data, disableWorker: true }) without standardFontDataUrl before later calling page.render(...).

Fix

Provide:

const standardFontDataUrl = new URL(
  "../node_modules/pdfjs-dist/standard_fonts/",
  import.meta.url,
).pathname;

and pass it into getDocument(...).

Why .pathname

Local verification showed that .href changed the failure into font-load warnings in this Node runtime path, while .pathname eliminated the warning both locally and in a live post-restart check.

Testing

  • corepack pnpm exec vitest run --config vitest.config.ts src/media/pdf-extract.test.ts src/agents/tools/pdf-tool.test.ts
    • 52 passed

Notes

  • AI-assisted change
  • Narrow PR: PDF extraction-fallback only
  • No full repo pnpm test run

@greptile-apps

greptile-apps Bot commented Mar 21, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR addresses the UnknownErrorException: Ensure that the standardFontDataUrl API parameter is provided warning emitted by PDF.js during Node fallback rendering by wiring standardFontDataUrl into the getDocument call and adding a regression test. The concept is correct, but the relative path used to locate the standard fonts directory has a depth error that will prevent fonts from actually loading at runtime.

  • Path traversal is one level too shallow: "../node_modules/pdfjs-dist/standard_fonts/" resolves from the compiled output at dist/media/pdf-extract.js to dist/node_modules/pdfjs-dist/standard_fonts/ — a directory that does not exist. The correct path is "../../node_modules/pdfjs-dist/standard_fonts/" to reach the repo-root node_modules/.
  • The original warning is suppressed but fonts may still not load: providing any truthy string for standardFontDataUrl silences the "parameter not provided" error, so local testing may appear clean while font fetches silently 404 at the wrong path.
  • Test does not guard against path regressions: getDocument is fully mocked and path existence is never verified with fs.existsSync, so the test passes regardless of whether the resolved directory exists.
  • The type declaration update in pdfjs-dist-legacy.d.ts is clean and correct.

Confidence Score: 2/5

  • Not safe to merge — the relative path resolves to a non-existent directory at runtime, meaning standard fonts will still fail to load despite the fix.
  • The fix correctly identifies the missing standardFontDataUrl parameter and the approach of computing a filesystem path from import.meta.url is sound. However, the relative path "../node_modules/..." is one ../ short of the repo root when evaluated from dist/media/pdf-extract.js at runtime. This means font file fetches will silently 404, leaving the underlying rendering problem present even though the specific error message is suppressed. The regression test also doesn't guard against this because it fully mocks getDocument and never checks the path's existence on disk. These together mean the PR does not fully achieve its stated goal and needs a one-character fix before merging.
  • src/media/pdf-extract.ts (line 52) — fix the relative path from "../node_modules/..." to "../../node_modules/..."
Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/media/pdf-extract.ts
Line: 52-53

Comment:
**Wrong depth in relative path — fonts won't be found at runtime**

The relative path `"../node_modules/pdfjs-dist/standard_fonts/"` is one level too shallow.

The compiled file lives at `dist/media/pdf-extract.js`, so `import.meta.url` at runtime resolves to `file:///…/dist/media/pdf-extract.js`. Resolving `../` from `dist/media/` gives `dist/`, not the repo root. The full resolved path would be:

```
/path/to/openclaw/dist/node_modules/pdfjs-dist/standard_fonts/
```

…which does not exist. The actual package is installed at `/path/to/openclaw/node_modules/pdfjs-dist/standard_fonts/`.

Reaching the repo root from `dist/media/` requires two levels up:

```suggestion
  const standardFontDataUrl = new URL("../../node_modules/pdfjs-dist/standard_fonts/", import.meta.url)
    .pathname;
```

This silently causes PDF.js to fail to load standard fonts even though the parameter is now wired in. The specific `"standardFontDataUrl API parameter is provided"` warning disappears (because the param is now truthy), but font fetches will 404 and PDF rendering quality may still be degraded. The test does not catch this because `getDocument` is fully mocked and the path's existence is never verified.

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: src/media/pdf-extract.test.ts
Line: 55-58

Comment:
**Test does not verify the resolved path exists on disk**

The assertions check that `standardFontDataUrl` is a non-empty string containing `"standard_fonts"`, but they never verify the path actually points to an existing directory. This means the test passes even when the path resolves to a non-existent location (e.g., `dist/node_modules/pdfjs-dist/standard_fonts/`).

Consider adding a `fs.existsSync` guard to catch path regressions:

```ts
import { existsSync } from "node:fs";
//
expect(existsSync(params.standardFontDataUrl)).toBe(true);
```

This would have caught the depth-mismatch in the relative path before the PR landed.

How can I resolve this? If you propose a fix, please make it concise.

Last reviewed commit: "Fix PDF fallback sta..."

Comment thread src/media/pdf-extract.ts Outdated
Comment on lines +52 to +53
const standardFontDataUrl = new URL("../node_modules/pdfjs-dist/standard_fonts/", import.meta.url)
.pathname;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Wrong depth in relative path — fonts won't be found at runtime

The relative path "../node_modules/pdfjs-dist/standard_fonts/" is one level too shallow.

The compiled file lives at dist/media/pdf-extract.js, so import.meta.url at runtime resolves to file:///…/dist/media/pdf-extract.js. Resolving ../ from dist/media/ gives dist/, not the repo root. The full resolved path would be:

/path/to/openclaw/dist/node_modules/pdfjs-dist/standard_fonts/

…which does not exist. The actual package is installed at /path/to/openclaw/node_modules/pdfjs-dist/standard_fonts/.

Reaching the repo root from dist/media/ requires two levels up:

Suggested change
const standardFontDataUrl = new URL("../node_modules/pdfjs-dist/standard_fonts/", import.meta.url)
.pathname;
const standardFontDataUrl = new URL("../../node_modules/pdfjs-dist/standard_fonts/", import.meta.url)
.pathname;

This silently causes PDF.js to fail to load standard fonts even though the parameter is now wired in. The specific "standardFontDataUrl API parameter is provided" warning disappears (because the param is now truthy), but font fetches will 404 and PDF rendering quality may still be degraded. The test does not catch this because getDocument is fully mocked and the path's existence is never verified.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/media/pdf-extract.ts
Line: 52-53

Comment:
**Wrong depth in relative path — fonts won't be found at runtime**

The relative path `"../node_modules/pdfjs-dist/standard_fonts/"` is one level too shallow.

The compiled file lives at `dist/media/pdf-extract.js`, so `import.meta.url` at runtime resolves to `file:///…/dist/media/pdf-extract.js`. Resolving `../` from `dist/media/` gives `dist/`, not the repo root. The full resolved path would be:

```
/path/to/openclaw/dist/node_modules/pdfjs-dist/standard_fonts/
```

…which does not exist. The actual package is installed at `/path/to/openclaw/node_modules/pdfjs-dist/standard_fonts/`.

Reaching the repo root from `dist/media/` requires two levels up:

```suggestion
  const standardFontDataUrl = new URL("../../node_modules/pdfjs-dist/standard_fonts/", import.meta.url)
    .pathname;
```

This silently causes PDF.js to fail to load standard fonts even though the parameter is now wired in. The specific `"standardFontDataUrl API parameter is provided"` warning disappears (because the param is now truthy), but font fetches will 404 and PDF rendering quality may still be degraded. The test does not catch this because `getDocument` is fully mocked and the path's existence is never verified.

How can I resolve this? If you propose a fix, please make it concise.

Comment thread src/media/pdf-extract.test.ts Outdated
Comment on lines +55 to +58
});
expect(typeof params.standardFontDataUrl).toBe("string");
expect(params.standardFontDataUrl).toContain("standard_fonts");
expect(params.standardFontDataUrl.endsWith("/standard_fonts/")).toBe(true);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Test does not verify the resolved path exists on disk

The assertions check that standardFontDataUrl is a non-empty string containing "standard_fonts", but they never verify the path actually points to an existing directory. This means the test passes even when the path resolves to a non-existent location (e.g., dist/node_modules/pdfjs-dist/standard_fonts/).

Consider adding a fs.existsSync guard to catch path regressions:

import { existsSync } from "node:fs";
// …
expect(existsSync(params.standardFontDataUrl)).toBe(true);

This would have caught the depth-mismatch in the relative path before the PR landed.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/media/pdf-extract.test.ts
Line: 55-58

Comment:
**Test does not verify the resolved path exists on disk**

The assertions check that `standardFontDataUrl` is a non-empty string containing `"standard_fonts"`, but they never verify the path actually points to an existing directory. This means the test passes even when the path resolves to a non-existent location (e.g., `dist/node_modules/pdfjs-dist/standard_fonts/`).

Consider adding a `fs.existsSync` guard to catch path regressions:

```ts
import { existsSync } from "node:fs";
//
expect(existsSync(params.standardFontDataUrl)).toBe(true);
```

This would have caught the depth-mismatch in the relative path before the PR landed.

How can I resolve this? If you propose a fix, please make it concise.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d8e89c656d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/media/pdf-extract.ts Outdated
Comment on lines +52 to +53
const standardFontDataUrl = new URL("../node_modules/pdfjs-dist/standard_fonts/", import.meta.url)
.pathname;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Resolve standardFontDataUrl from both source and dist layouts

When this code runs from source rather than a built install, import.meta.url is .../src/media/pdf-extract.ts, so ../node_modules/pdfjs-dist/standard_fonts/ resolves to src/node_modules/... instead of the repo root. The main dev entrypoints (pnpm openclaw / pnpm dev via scripts/run-node.mjs) still execute source files, so scanned/image-only PDFs in those workflows will keep failing once pdf.js tries to load fallback fonts. The added test only asserts that some standard_fonts string is passed, so it won't catch this broken path.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4174400354

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/media/pdf-extract.test.ts Outdated
});
expect(typeof params.standardFontDataUrl).toBe("string");
expect(params.standardFontDataUrl).toContain("standard_fonts");
expect(params.standardFontDataUrl.endsWith("standard_fonts/")).toBe(true);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Make path assertion separator-agnostic

fileURLToPath(...) returns Windows-style paths (ending in \standard_fonts\), so this hardcoded "standard_fonts/" suffix check fails on Windows even when standardFontDataUrl is correct. Because this PR touches src/**, the Windows shard in .github/workflows/ci.yml runs pnpm test, so this assertion can block CI for a non-bug in runtime behavior; please make the suffix check accept both separators (or assert via path.basename/dirname).

Useful? React with 👍 / 👎.

@anyech
anyech force-pushed the fix/pdf-standard-font-data-url branch from 4174400 to c76b9e5 Compare March 31, 2026 07:08
@anyech

anyech commented Apr 19, 2026

Copy link
Copy Markdown
Contributor Author

Thanks — I checked the current failing CI lane by looking at the check-run annotations first rather than starting with the full workflow logs.

The concrete failing annotation on the current head points to:

  • src/cli/cron-cli/shared.test.ts:173
  • getCronChannelOptions > falls back to a generic channel placeholder when no plugins are loaded
  • expected: last|<channel-id>
  • received: last|telegram|signal

I then rechecked that seam in a disposable checkout of this PR branch and ran the narrowest local verification I could exercise:

corepack pnpm exec vitest run --config vitest.config.ts src/cli/cron-cli/shared.test.ts

That targeted test passes locally in the disposable checkout.

I also diffed this PR branch against the current mainline. The branch only touches:

  • src/media/pdf-extract.test.ts
  • src/media/pdf-extract.ts
  • src/types/pdfjs-dist-legacy.d.ts

It does not touch src/cli/cron-cli/shared.ts or src/cli/cron-cli/shared.test.ts, so at the moment this looks more like an adjacent or stale CI failure than something introduced by the standardFontDataUrl change itself.

Caveat: this is not a clean latest-version stage repro of the Windows failure path. It is a narrower annotation-first read plus isolated disposable-checkout verification.

Given that, I would prefer not to widen this PR to fix an apparently unrelated cron-cli test failure. If helpful, I can rerun the Windows lane or handle that as a separate follow-up, but I would rather keep this PR scoped to the PDF fix.

@anyech

anyech commented Apr 19, 2026

Copy link
Copy Markdown
Contributor Author

Thanks — I checked the current failing CI lane by looking at the check-run annotations first rather than starting with the full workflow logs.

The concrete failing annotation on the current head points to:

  • src/cli/cron-cli/shared.test.ts:173
  • getCronChannelOptions > falls back to a generic channel placeholder when no plugins are loaded
  • expected: last|<channel-id>
  • received: last|telegram|signal

I then rechecked that seam in a disposable checkout of this PR branch and ran the narrowest local verification I could exercise:

corepack pnpm exec vitest run --config vitest.config.ts src/cli/cron-cli/shared.test.ts

That targeted test passes locally in the disposable checkout.

I also diffed this PR branch against the current mainline. The branch only touches:

  • src/media/pdf-extract.test.ts
  • src/media/pdf-extract.ts
  • src/types/pdfjs-dist-legacy.d.ts

It does not touch src/cli/cron-cli/shared.ts or src/cli/cron-cli/shared.test.ts, so from this narrow check it looks more likely to be adjacent than introduced by the standardFontDataUrl change itself.

Caveat: this is not a clean latest-version stage repro of the Windows failure path. It is a narrower annotation-first read plus isolated disposable-checkout verification.

Given that, I would keep this PR scoped to the PDF fix unless stronger evidence ties the cron-cli failure to this change. If helpful, I can rerun the Windows lane or handle that as a separate follow-up.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2e6534e5c9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/media/pdf-extract.ts
const pdf = await getDocument({ data: new Uint8Array(buffer), disableWorker: true }).promise;
const pdfJsModule = await loadPdfJsModule();
const standardFontDataUrl = fileURLToPath(
new URL("../../node_modules/pdfjs-dist/standard_fonts/", import.meta.url),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Resolve standard-font path from package location

standardFontDataUrl is computed with new URL("../../node_modules/...", import.meta.url), which only works when the emitted file stays two directories below the package root. This repo’s build uses tsdown without unbundle: true (tsdown.config.ts), so bundled output can place this code in root-level entry/chunk files; in that layout ../../node_modules resolves outside the OpenClaw package and the font directory is missing, so Node fallback rendering still emits the same PDF.js standard-font warnings in built installs. Resolve from pdfjs-dist/package.json (e.g. via require.resolve) instead of assuming a fixed relative depth.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2e6534e5c9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/media/pdf-extract.ts
const pdf = await getDocument({ data: new Uint8Array(buffer), disableWorker: true }).promise;
const pdfJsModule = await loadPdfJsModule();
const standardFontDataUrl = fileURLToPath(
new URL("../../node_modules/pdfjs-dist/standard_fonts/", import.meta.url),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Resolve standard font path from pdfjs module location

Computing standardFontDataUrl via new URL("../../node_modules/...", import.meta.url) is still layout-dependent and can miss in bundled output: this repo builds with bundled mode (not unbundle), and we already expect hashed top-level dist/*.js chunks in tests (for example src/plugin-sdk/root-alias.test.ts), so if this module is emitted at dist/*.js the resulting path becomes <package-parent>/node_modules/node_modules/pdfjs-dist/standard_fonts, which does not exist. In that case PDF.js cannot load standard fonts and the fallback renderer reverts to the same warning-prone behavior this change is trying to fix.

Useful? React with 👍 / 👎.

@openclaw-barnacle

Copy link
Copy Markdown

Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch.

@openclaw-barnacle

Copy link
Copy Markdown

Closing this PR because it looks dirty (too many unrelated or unexpected changes). This usually happens when a branch picks up unrelated commits or a merge went sideways. Please recreate the PR from a clean branch.

@chatgpt-codex-connector

Copy link
Copy Markdown
Contributor

💡 Codex Review

const standardFontDataUrl = fileURLToPath(
new URL("../../node_modules/pdfjs-dist/standard_fonts/", import.meta.url),

P1 Badge Resolve standard fonts path from package root

Compute standardFontDataUrl without assuming this file stays two directories under the package root. The bundled build emits root-level dist/*.js entry/chunk files, and when this code runs from those artifacts ../../node_modules/pdfjs-dist/standard_fonts/ resolves to the parent package tree (for example .../node_modules/node_modules/...), so the directory is missing and PDF.js still cannot load standard fonts during fallback rendering. Resolving from the installed pdfjs-dist package location avoids this layout-dependent failure.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling app: android App: android app: ios App: ios app: macos App: macos channel: bluebubbles Channel integration: bluebubbles channel: discord Channel integration: discord channel: feishu Channel integration: feishu channel: googlechat Channel integration: googlechat channel: imessage Channel integration: imessage channel: irc channel: line Channel integration: line channel: matrix Channel integration: matrix channel: mattermost Channel integration: mattermost channel: msteams Channel integration: msteams channel: nextcloud-talk Channel integration: nextcloud-talk channel: nostr Channel integration: nostr channel: qa-channel Channel integration: qa-channel channel: qqbot channel: signal Channel integration: signal channel: slack Channel integration: slack channel: telegram Channel integration: telegram channel: tlon Channel integration: tlon channel: twitch Channel integration: twitch channel: voice-call Channel integration: voice-call channel: whatsapp-web Channel integration: whatsapp-web channel: zalo Channel integration: zalo channel: zalouser Channel integration: zalouser cli CLI command changes commands Command implementations docker Docker and sandbox tooling docs Improvements or additions to documentation extensions: acpx extensions: anthropic extensions: cloudflare-ai-gateway extensions: codex extensions: device-pair extensions: diagnostics-otel Extension: diagnostics-otel extensions: duckduckgo extensions: kilocode extensions: kimi-coding extensions: llm-task Extension: llm-task extensions: lmstudio extensions: lobster Extension: lobster extensions: memory-core Extension: memory-core extensions: memory-lancedb Extension: memory-lancedb extensions: memory-wiki extensions: minimax extensions: moonshot extensions: openai extensions: qa-lab extensions: tavily extensions: tencent extensions: tokenjuice Changes to the bundled tokenjuice extension extensions: vercel-ai-gateway extensions: webhooks gateway Gateway runtime scripts Repository scripts security Security documentation size: XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: pdf extraction fallback renderer omits standardFontDataUrl and emits PDF.js warnings in Node