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:
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 <Document> was committed successfully:
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.:
await renderToBuffer(<MyDocument />, {
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
Feature request: expose a way to override the reconciler's error handling instead of hardcoding
console.errorSummary
@react-pdf/reconcilerwires React's root-levelonUncaughtError,onCaughtError, andonRecoverableErrorcallbacks directly toconsole.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 rawconsole.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-argumentconsole.error(Error, { componentStack })call.Current behavior
In the reconciler wrapper (e.g.
@react-pdf/reconciler'sreconciler-33.js, used for React 19.2+), the container is created like this:All three error-handling hooks are hardcoded to
console.error.Separately,
renderToBuffer/renderToStream/toBuffer(in@react-pdf/renderer) assume the root<Document>was committed successfully:If the tree fails to render (because a component threw and was routed to
onUncaughtError),container.documentis never set, andrender()throws a generic, unrelated-looking:Impact
When a component in the document tree throws during render (e.g. a bad prop, an unexpected
nullwhere the app's types assumed non-null), the actual error and its component stack are:console.errorcall the consuming app cannot intercept, tag, or route.TypeErrorfromrender()'s null-check, which is the only error that actually propagates up to the caller (e.g. viarenderToBuffer'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, andonRecoverableError(or a single unifiedonError) as options onrenderToBuffer/renderToStream/renderToFile/pdf(), so consumers can plug in their own logger, e.g.:Even just accepting an optional
onError/onRenderErrorcallback that defaults toconsole.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 whethercontainer.documentisnulland 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.0renderToBuffer), not browser