You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When an exception is thrown during the rendering of a dynamic element, the resulting stack trace shows the structural shape of the render but tells you almost nothing useful about where the error occurred.
The current pain
A simple formatting error in an NGTextField produces a stack trace like:
java.lang.IllegalArgumentException: Cannot format given Object as a Number
at java.base/java.text.DecimalFormat.format(DecimalFormat.java:584)
at ng.appserver.templating.elements.NGTextField.appendToResponse(NGTextField.java:117)
at ng.appserver.templating.NGElement.appendOrTraverse(NGElement.java:23)
at ng.appserver.templating.elements.NGDynamicGroup.appendChildrenToResponse(...)
at ng.appserver.templating.elements.NGConditional.appendChildrenToResponse(...)
[...many more framework frames, repeating patterns...]
at ng.appserver.NGComponentRequestHandler.handleRequest(...)
From this you know: anNGTextFieldsomewhere failed to format some number. You don't know:
Which page is being rendered
Which component file the text field lives in (a typical app has many)
The element ID path that identifies which text field
Which binding failed (value? numberformat?)
The actual value that failed to format
The component instance state at the time
Which row of which repetition, if any
Debugging means searching the codebase for plausible candidates and guessing.
The proposal
Wrap exceptions at the element-traversal boundary with positional context. Minimal sketch:
NGRenderException: Render failed: NGTextField at element ID 3.2.1.0.4 in component MyPage
caused by: java.lang.IllegalArgumentException: Cannot format given Object as a Number
[original stack trace preserved as cause]
Three things are essential:
Preserve the original exception's stack trace as the cause. Never throw away the underlying issue.
Element ID path in the message. Uniquely identifies the position in the render tree; can be correlated with the rendered DOM.
Component class (not just element type). "NGTextField in MyPage" is dramatically more actionable than "NGTextField."
The re-throw guard ("if already an NGRenderException, pass it through") ensures only the innermost element wraps. The outer frames don't re-wrap, so the rich context surfaces at the top of the trace rather than getting buried under N layers of "render failed: NGDynamicGroup at..."
Going further (optional)
A meaningful enhancement: include which binding failed. Requires per-element opt-in — each element wraps its binding evaluations:
NGRenderException: Render failed: NGTextField at element ID 3.2.1.0.4 in component MyPage
caused by: NGBindingException: binding 'value' (resolved to \"hello world\") failed
caused by: java.lang.IllegalArgumentException: Cannot format given Object as a Number
Worth doing for the most-used elements (NGString, NGTextField, NGRepetition, conditionals). Other elements can adopt incrementally.
Future directions
Source positions on elements — when templates are parsed, stamp each element with file + line. Error message includes MyPage.wo:42. Natural extension once template compilation (Compile templates to Java classes #60) lands; generated classes know their source positions.
Trace cleanup — filter framework-internal frames (appendOrTraverse, appendChildrenToResponse, the NGDynamicGroup repetition) from the displayed stack. Less destructive variant: annotate them so IDEs can collapse rather than removing entirely.
Cursor-based — once the render redesign lands and elements receive a cursor parameter, position info reads off the cursor in one place. The element-by-element wrapping above becomes a single top-level handler.
Inline error rendering — Parsley-style (in WO) renders the error visually at its position in the rendered page. Complementary to stack-trace enrichment; one is for logs, the other for development browsing. See related design conversations.
Scope for v1
The minimal version is genuinely an afternoon's work: add the wrap in appendOrTraverse, define NGRenderException. Per-binding wrapping in popular elements is an optional follow-up.
The cost is essentially zero (one catch block on the rendering hot path; if rendering succeeds the overhead is negligible) and the debug-time payoff is large.
When an exception is thrown during the rendering of a dynamic element, the resulting stack trace shows the structural shape of the render but tells you almost nothing useful about where the error occurred.
The current pain
A simple formatting error in an
NGTextFieldproduces a stack trace like:From this you know: an
NGTextFieldsomewhere failed to format some number. You don't know:value?numberformat?)Debugging means searching the codebase for plausible candidates and guessing.
The proposal
Wrap exceptions at the element-traversal boundary with positional context. Minimal sketch:
About 30 lines of code. The error becomes:
Three things are essential:
The re-throw guard ("if already an NGRenderException, pass it through") ensures only the innermost element wraps. The outer frames don't re-wrap, so the rich context surfaces at the top of the trace rather than getting buried under N layers of "render failed: NGDynamicGroup at..."
Going further (optional)
A meaningful enhancement: include which binding failed. Requires per-element opt-in — each element wraps its binding evaluations:
Error becomes:
Worth doing for the most-used elements (
NGString,NGTextField,NGRepetition, conditionals). Other elements can adopt incrementally.Future directions
MyPage.wo:42. Natural extension once template compilation (Compile templates to Java classes #60) lands; generated classes know their source positions.appendOrTraverse,appendChildrenToResponse, theNGDynamicGrouprepetition) from the displayed stack. Less destructive variant: annotate them so IDEs can collapse rather than removing entirely.Scope for v1
The minimal version is genuinely an afternoon's work: add the wrap in
appendOrTraverse, defineNGRenderException. Per-binding wrapping in popular elements is an optional follow-up.The cost is essentially zero (one catch block on the rendering hot path; if rendering succeeds the overhead is negligible) and the debug-time payoff is large.