Skip to content

fix: fail the task chain when time-series retrieval fails (#2063) - #2065

Merged
jcschaff merged 2 commits into
masterfrom
fix/timeseries-error-masking
Sep 4, 2026
Merged

fix: fail the task chain when time-series retrieval fails (#2063)#2065
jcschaff merged 2 commits into
masterfrom
fix/timeseries-error-masking

Conversation

@jcschaff

@jcschaff jcschaff commented Sep 4, 2026

Copy link
Copy Markdown
Member

Fixes #2063. Takes the inverted-default approach rather than the contained one.

What was wrong

PDEDataViewer.TimeSeriesDataRetrievalTask caught every exception, recorded it under
StringKey_timeSeriesJobException and returned normally:

} catch (Exception exception) {
    hashTable.put(StringKey_timeSeriesJobException, exception);
}   // never rethrown

Four of its six callers never read that key. The dispatcher therefore treated the task as
successful and ran the next one against a null result. What the user saw was an NPE in an
unrelated plotting task, with the real cause thrown away. A cancellation went the same way,
since UserCancelException was swallowed along with everything else.

call site before
PdeTimePlotMultipleVariablesPanel:201 NPE — the reported crash
PDEDataViewer:1177 NPE — plotSpaceStats dereferences getVariableNames() immediately
PDEDataViewer:3013 same NPE via plotSpaceStats
PDEDataViewer:3072 opened a save-file chooser first, so the user named a movie file for data that was never retrieved, then failed
PDEDataViewer:2624 checked, and did nothing but rethrow
KymographPanel:2035 checked, and handled it locally

The fix

Throwing is now the default. ClientTaskDispatcher already does exactly the right thing with a
thrown exception — recordException routes UserCancelException to TASK_ABORTED_BY_USER and
stays quiet, while anything else becomes TASK_ABORTED_BY_ERROR and an error dialog naming the
real cause — so the four broken callers are fixed without touching them, including the
save-file-chooser ordering at 3072.

KymographPanel is the reason a blanket rethrow would have been wrong. It reports failures
inside the panel via failMethod() and also folds in TASK_ABORTED_BY_USER; rethrowing would
have skipped task3 and lost that handling entirely. It opts back in through a new
TimeSeriesDataRetrievalTask.recordingFailure(...) factory.

The matching check in PDEDataViewer:2624's task is now unreachable, so it is removed rather
than left implying the key can still be set.

One thing that made this safe to reason about: the TimeSeriesDataJobListener DATA_COMPLETE /
DATA_FAILURE hashtable writes are commented out, so the synchronous task is the only writer
of either key — there is no async race here.

Tests

New TimeSeriesDataRetrievalTaskTest (5 tests, Fast): a failure and a cancellation both abort
the chain carrying the original cause, recordingFailure() still records instead of throwing,
a success publishes its results, and the data-job listener is removed however the task ends.

I checked them against the old behaviour rather than assuming they bite — reverting just the
throw makes 3 of the 5 fail, and the 2 that pass are the 2 that should (recordingFailure
and the success path).

Verification

  • TimeSeriesDataRetrievalTaskTest — 5/5, and 3/5 fail without the fix
  • vcell-client Fast — 36 tests, 0 failures, 0 errors
  • full mvn compile test-compile across all modules — clean

Not covered by automated tests: the kymograph's in-panel failure message and the dispatcher's
error dialog are Swing paths. The kymograph call site is a one-line change to the factory with
its downstream logic untouched, so the risk is confined to that panel still receiving the
recorded exception — which the test asserts directly.

🤖 Generated with Claude Code

https://claude.ai/code/session_01D71LBYmQNf5J94wPqr81Jx

jcschaff and others added 2 commits September 4, 2026 14:10
TimeSeriesDataRetrievalTask caught every exception, recorded it under
StringKey_timeSeriesJobException and returned normally. Four of its six callers
never read that key, so the next task ran against a null result: the user saw an
NPE in an unrelated plotting task and the real cause was discarded. A
cancellation went the same way, because UserCancelException was swallowed with
everything else.

Failing the chain is now the default, which is what five of the six callers
want. ClientTaskDispatcher already does the right thing with a thrown exception
- UserCancelException goes to TASK_ABORTED_BY_USER quietly, anything else raises
an error dialog naming the real cause - so those callers need no change of their
own. This also fixes the surface-movie path, which used to open a save-file
chooser and only then discover it had no data.

KymographPanel is the exception: it reports failures inside the panel through
failMethod() and folds in TASK_ABORTED_BY_USER, so it opts back in through the
new recordingFailure() factory. The matching check in PDEDataViewer's time-plot
task is now unreachable and has been removed rather than left to suggest the key
can still be set.

Fixes #2063

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D71LBYmQNf5J94wPqr81Jx
Five tests over TimeSeriesDataRetrievalTask: a failure and a cancellation both
abort the chain carrying the original cause, recordingFailure() still records
instead of throwing for KymographPanel, a success publishes its results, and the
data-job listener is removed however the task ends.

Checked against the old behaviour: three of the five fail without the fix, and
the two that pass are the two that should.

Refs #2063

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D71LBYmQNf5J94wPqr81Jx
@jcschaff
jcschaff merged commit b1340ea into master Sep 4, 2026
9 checks passed
@jcschaff
jcschaff deleted the fix/timeseries-error-masking branch September 4, 2026 19:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TimeSeriesDataRetrievalTask swallows retrieval failures; 4 of 6 call sites then NPE on the null result

1 participant