diegomura / diegomura/react-pdf
expose a way to override the reconciler's error handling instead of hardcoding `console.error`
- Dominant language
- TypeScript
- Stars
- 16.8k
- Forks
- 1.3k
- Avg merge
- 5h 6m
- Merged PRs (30d)
- 52
Description
## Feature request: expose a way to override the reconciler's error handling instead of hardcoding `console.error`
### Summary
`@react-pdf/reconciler` wires React's root-level `onUncaughtError`, `onCaughtError`, and `onRecoverableError` callbacks directly to `console.error`, with no way for a consumer to override them. This means any render-time exception in the document tree is only ever surfaced via a raw `console.error(error, errorInfo)` call — it can't be routed through the app's own logger, can't be tagged with request context, and in server environments it can be silently dropped by logging pipelines that don't expect a two-argument `console.error(Error, { componentStack })` call.
### Current behavior
In the reconciler wrapper (e.g. `@react-pdf/reconciler`'s `reconciler-33.js`, used for React 19.2+), the container is created like this:
```js
const S = console.error;
// ...
createContainer: (containerInfo) =>
reconciler.createContainer(
containerInfo,
ConcurrentRoot,
null,
false,
null,
"",
S, // onUncaughtError
S, // onCaughtError
S, // onRecoverableError
() => {},
null
);
```
All three error-handling hooks are hardcoded to `console.error`.
Separately, `renderToBuffer`/`renderToStream`/`toBuffer` (in `@react-pdf/renderer`) assume the root `` was committed successfully:
```js
const render = async function (compress = true) {
const props = container.document.props || {};
// ...
};
```
If the tree fails to render (because a component threw and was routed to `onUncaughtError`), `container.document` is never set, and `render()` throws a generic, unrelated-looking:
```
TypeError: Cannot read properties of null (reading 'props')
```
### Impact
When a component in the document tree throws during render (e.g. a bad prop, an unexpected `null` where the app's types assumed non-null), the actual error and its component stack are:
- Only ever logged via a raw `console.error` call the consuming app cannot intercept, tag, or route.
- Immediately followed by a second, unrelated `TypeError` from `render()`'s null-check, which is the *only* error that actually propagates up to the caller (e.g. via `renderToBuffer`'s returned promise).
In practice this means server-side consumers only ever see the second, generic error in their logs/error tracking, with no way to get the real one — even though React already computed it and had a hook designed exactly for this purpose.
### Proposed solution
Expose `onUncaughtError`, `onCaughtError`, and `onRecoverableError` (or a single unified `onError`) as options on `renderToBuffer`/`renderToStream`/`renderToFile`/`pdf()`, so consumers can plug in their own logger, e.g.:
```js
await renderToBuffer(, {
onUncaughtError: (error, errorInfo) => {
myLogger.error(error, { componentStack: errorInfo.componentStack });
},
});
```
Even just accepting an optional `onError`/`onRenderError` callback that defaults to `console.error` (preserving current behavior) would unblock this.
### Alternative / smaller ask
If wiring custom handlers through the public API is out of scope, even having `render()` check whether `container.document` is `null` and throw a clearer error (e.g. "Document failed to render — see the error logged above via console.error") would make this failure mode much easier to diagnose without a library change.
### Environment
- `@react-pdf/renderer`: 4.5.1
- `@react-pdf/reconciler`: 2.0.0
- React: 19.2.4
- Node.js server-side rendering (`renderToBuffer`), not browser
Contributor guide
Research direction
Start in the reconciler wrapper at reconciler-33.js, then trace createContainer and the renderToBuffer, renderToStream, and toBuffer paths in @react-pdf/renderer. Compare how the three React error callbacks and the null container.document case are handled. Done means the chosen error-handling option is defined for the public rendering entry points and render failures expose the intended error behavior without breaking the default console.error path.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- node.js, react, typescript
- Domain
- backend
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100