Skip to content

fix: keep component formatting failures from hiding state tree errors - #25459

Open
totally-not-ai[bot] wants to merge 3 commits into
mainfrom
fix-state-node-owner-component-formatting-failures
Open

fix: keep component formatting failures from hiding state tree errors#25459
totally-not-ai[bot] wants to merge 3 commits into
mainfrom
fix-state-node-owner-component-formatting-failures

Conversation

@totally-not-ai

@totally-not-ai totally-not-ai Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

StateNode builds human-readable descriptions of a node and its owner component for error and log messages. Both code paths call back into application code, which can throw and mask the message that was actually being reported.

The problem

  • formatOwnerComponentToString() is used when constructing the IllegalStateException about moving a node from one state tree to another. It calls the component's toString() and — through the ComponentTracker weak maps — its hashCode(). If either throws, that exception propagated out instead of the IllegalStateException, so the user saw the misbehaving toString() rather than the real problem (a component moved between UIs).
  • describe() has the same exposure: ComponentUtil.getRouteComponent() walks getParent(), and the ComponentTracker lookup goes through the component's hashCode(). Its javadoc already promised a description "for a log message", so throwing from it turns a log message into an error.

The fix

Both methods now catch RuntimeException and describe the failure instead of propagating it:

  • formatOwnerComponentToString() returns unavailable, describing the component of type <class> threw <exception class>, so the surrounding IllegalStateException still reaches the user with the component type and the reason the description is missing.
  • describe() appends , describing it further threw <exception class> to the details it has already gathered (node id, tag, component class, …) rather than losing them.

In both cases the swallowed exception is logged at debug level so it is still recoverable when needed. The describe() javadoc now states explicitly that the method never throws.

Tests

  • ComponentTest.cannotMoveComponentsToOtherUI_componentToStringThrows_originalErrorIsReported — adds a component whose toString() throws and asserts the resulting IllegalStateException names both the component class and the thrown exception type.
  • StateNodeTest.describe_applicationCodeThrows_failureDescribedWithDetailsSoFar — a component whose getParent() throws; describe() still returns a description naming the component class and the exception.

The existing assertions were also relaxed to check for the essential parts (component class, exception type) instead of matching the full message, so they no longer break on wording changes.

formatOwnerComponentToString calls application code, e.g. the component's
toString() and, via the ComponentTracker weak maps, its hashCode(). When
that code throws, its exception replaced the IllegalStateException about
moving a node between state trees, hiding the actual problem.

Catch runtime exceptions and describe the failure instead, logging the
exception at debug level.
describe() reaches into application code too: getRouteComponent walks
getParent(), and the ComponentTracker lookup uses the component's
hashCode(). Since the description is meant for log messages, catch
runtime exceptions and append a note about the failure to the details
gathered so far instead of propagating it.
Comment thread flow-server/src/test/java/com/vaadin/flow/component/ComponentTest.java Outdated
Matching the full message made the tests break on any wording change,
while what matters is that the description names the component class and
the exception that was thrown.
@github-actions github-actions Bot added the +0.0.1 label Sep 3, 2026
@sonarqubecloud

sonarqubecloud Bot commented Sep 3, 2026

Copy link
Copy Markdown

@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Test Results

 1 433 files  ±0   1 517 suites  ±0   1h 33m 59s ⏱️ - 2m 44s
11 862 tests +2  11 795 ✅ +2  67 💤 ±0  0 ❌ ±0 
12 181 runs  +2  12 113 ✅ +2  68 💤 ±0  0 ❌ ±0 

Results for commit e5c0d27. ± Comparison against base commit 9e33987.

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant