Skip to content

fix(frontend, v1.2): improve static error msgs to be user-friendly for unco… - #7012

Open
Yicong-Huang wants to merge 1 commit into
apache:release/v1.2from
Yicong-Huang:backport/6288-improve-static-error-msgs-to-be-user-fri-v1.2
Open

fix(frontend, v1.2): improve static error msgs to be user-friendly for unco… #7012
Yicong-Huang wants to merge 1 commit into
apache:release/v1.2from
Yicong-Huang:backport/6288-improve-static-error-msgs-to-be-user-fri-v1.2

Conversation

@Yicong-Huang

Copy link
Copy Markdown
Contributor

What changes were proposed in this PR?

Backport of #6288 to release/v1.2, cherry-picked from 8a51c56.

Source fix only. Test changes from #6288 were omitted (the touched specs do not exist on release/v1.2 or depend on main-only test infrastructure); only the source fix is carried over, per maintainer guidance.

Any related issues, documentation, discussions?

Backport of #6288. Originally linked #2735.

How was this PR tested?

Release-branch CI runs on this PR. Source change cherry-picked cleanly; test changes intentionally dropped.

Was this PR authored or co-authored using generative AI tooling?

Yes — backport prepared with Claude Code (mechanical cherry-pick + conflict resolution; the change itself is #6288 by its original author).

🤖 Generated with Claude Code

apache#6288)

### What changes were proposed in this PR?
This PR overhauls the static error validation and error messaging across
Texera operators to drastically improve the experience for new users
during workflow editing.

Previously, if a user dragged an operator (like `Projection`, `MySQL`,
or `CSV File Scan`) onto the canvas without configuring it yet, the
system would immediately throw intimidating and cryptic Java exceptions
(e.g., `IllegalArgumentException`, `SQLException: No suitable driver
found`, or `/texera (Is a directory)`).

Changes proposed:
1. **Frontend Global Catch-all**: Updated the regex logic in
`error-frame.component.ts` to cleanly strip internal Java prefixes (like
`java.lang.IllegalArgumentException:` or `requirement failed:`) so the
user only sees the human-readable part of the error message.
2. **Projection Operator**: Updated `ProjectionOpDesc` to explicitly
prompt the user to select an attribute.
3. **Database Sources**: Updated `SQLSourceOpDesc` (and
`AsterixDBSourceOpDesc`) to intercept empty database configuration
fields and return actionable UI instructions (e.g., "Please enter a
valid host name for the database in the properties panel").
4. **File Scan Sources**: Updated all file scan operators
(`ParallelCSV`, `JSONL`, `Arrow`, etc.) to gracefully catch directory
selections and missing files before attempting physical reads, replacing
IOExceptions with clear prompts to select a valid file from the
right-hand panel.

### Any related issues, documentation, discussions?
Issue#2735

### How was this PR tested?
Tested manually by rebuilding the `workflow-compiling-service` and
dragging every modified operator (Projection, MySQL, PostgreSQL,
AsterixDB, CSV, JSONL, Arrow) onto a new workflow canvas. Verified that
the Result Panel correctly displays the newly formatted, user-friendly
instructions instead of raw Java exceptions.

### Was this PR authored or co-authored using generative AI tooling?
Generated-by: Antigravity (DeepMind)

**Tests:**
<img width="890" height="560" alt="WhatsApp Image 2026-07-09 at 12 56
49"
src="https://github.com/user-attachments/assets/4dd8f144-49c4-439f-be9b-89fbbfc6f8dd"
/>

--------------------------------------------------------------------------------------------------------------------------------------
<img width="1143" height="811" alt="image"
src="https://github.com/user-attachments/assets/463ec68b-636c-4ff9-ba65-f907f229c79d"
/>

--------------------------------------------------------------------------------------------------------------------------------------
<img width="1544" height="639" alt="image"
src="https://github.com/user-attachments/assets/923bc582-d825-4b15-8b00-6fff1f651642"
/>

--------------------------------------------------------------------------------------------------------------------------------------
<img width="1397" height="709" alt="image"
src="https://github.com/user-attachments/assets/731510a0-fba1-4a0f-86fd-12b2766e8e17"
/>

--------------------------------------------------------------------------------------------------------------------------------------

---------

(backported from commit 8a51c56)

Co-authored-by: probe <probe@x>
@Yicong-Huang Yicong-Huang added the release/v1.2 back porting to release/v1.2 label Jul 29, 2026
@Yicong-Huang
Yicong-Huang requested a review from xuang7 July 29, 2026 04:44
@github-actions github-actions Bot added engine frontend Changes related to the frontend GUI common platform Non-amber Scala service paths labels Jul 29, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Automated Reviewer Suggestions

Based on the git blame history of the changed files, we recommend the following reviewers:

  • Contributors with relevant context: @mengw15, @officialasishkumar
    You can notify them by mentioning @mengw15, @officialasishkumar in a comment.

@Yicong-Huang Yicong-Huang removed the release/v1.2 back porting to release/v1.2 label Jul 29, 2026
@codecov-commenter

codecov-commenter commented Jul 29, 2026

Copy link
Copy Markdown

❌ 2 Tests Failed:

Tests completed Failed Passed Skipped
3013 2 3011 9
View the top 2 failed test(s) by shortest run time
org.apache.texera.workflow.WorkflowCompilerSpec::WorkflowCompiler in strict mode (no errorList) should throw when a scan source has no fileName set
Stack Traces | 0.002s run time
org.scalatest.exceptions.TestFailedException: "[No file selected. Please select a file from the 'File' dropdown in the right panel.]" did not equal "[no input file name]"
	at org.scalatest.Assertions.newAssertionFailedException(Assertions.scala:472)
	at org.scalatest.Assertions.newAssertionFailedException$(Assertions.scala:471)
	at org.scalatest.Assertions$.newAssertionFailedException(Assertions.scala:1231)
	at org.scalatest.Assertions$AssertionsHelper.macroAssert(Assertions.scala:1295)
	at org.apache.texera.workflow.WorkflowCompilerSpec.$anonfun$new$15(WorkflowCompilerSpec.scala:210)
	at org.scalatest.OutcomeOf.outcomeOf(OutcomeOf.scala:85)
	at org.scalatest.OutcomeOf.outcomeOf$(OutcomeOf.scala:83)
	at org.scalatest.OutcomeOf$.outcomeOf(OutcomeOf.scala:104)
	at org.scalatest.Transformer.apply(Transformer.scala:22)
	at org.scalatest.Transformer.apply(Transformer.scala:20)
	at org.scalatest.flatspec.AnyFlatSpecLike$$anon$5.apply(AnyFlatSpecLike.scala:1832)
	at org.scalatest.TestSuite.withFixture(TestSuite.scala:196)
	at org.scalatest.TestSuite.withFixture$(TestSuite.scala:195)
	at org.scalatest.flatspec.AnyFlatSpec.withFixture(AnyFlatSpec.scala:1686)
	at org.scalatest.flatspec.AnyFlatSpecLike.invokeWithFixture$1(AnyFlatSpecLike.scala:1830)
	at org.scalatest.flatspec.AnyFlatSpecLike.$anonfun$runTest$1(AnyFlatSpecLike.scala:1842)
	at org.scalatest.SuperEngine.runTestImpl(Engine.scala:306)
	at org.scalatest.flatspec.AnyFlatSpecLike.runTest(AnyFlatSpecLike.scala:1842)
	at org.scalatest.flatspec.AnyFlatSpecLike.runTest$(AnyFlatSpecLike.scala:1824)
	at org.scalatest.flatspec.AnyFlatSpec.runTest(AnyFlatSpec.scala:1686)
	at org.scalatest.flatspec.AnyFlatSpecLike.$anonfun$runTests$1(AnyFlatSpecLike.scala:1900)
	at org.scalatest.SuperEngine.$anonfun$runTestsInBranch$1(Engine.scala:413)
	at scala.collection.immutable.List.foreach(List.scala:323)
	at org.scalatest.SuperEngine.traverseSubNodes$1(Engine.scala:401)
	at org.scalatest.SuperEngine.runTestsInBranch(Engine.scala:390)
	at org.scalatest.SuperEngine.$anonfun$runTestsInBranch$1(Engine.scala:427)
	at scala.collection.immutable.List.foreach(List.scala:323)
	at org.scalatest.SuperEngine.traverseSubNodes$1(Engine.scala:401)
	at org.scalatest.SuperEngine.runTestsInBranch(Engine.scala:396)
	at org.scalatest.SuperEngine.runTestsImpl(Engine.scala:475)
	at org.scalatest.flatspec.AnyFlatSpecLike.runTests(AnyFlatSpecLike.scala:1900)
	at org.scalatest.flatspec.AnyFlatSpecLike.runTests$(AnyFlatSpecLike.scala:1899)
	at org.scalatest.flatspec.AnyFlatSpec.runTests(AnyFlatSpec.scala:1686)
	at org.scalatest.Suite.run(Suite.scala:1114)
	at org.scalatest.Suite.run$(Suite.scala:1096)
	at org.scalatest.flatspec.AnyFlatSpec.org$scalatest$flatspec$AnyFlatSpecLike$$super$run(AnyFlatSpec.scala:1686)
	at org.scalatest.flatspec.AnyFlatSpecLike.$anonfun$run$1(AnyFlatSpecLike.scala:1945)
	at org.scalatest.SuperEngine.runImpl(Engine.scala:535)
	at org.scalatest.flatspec.AnyFlatSpecLike.run(AnyFlatSpecLike.scala:1945)
	at org.scalatest.flatspec.AnyFlatSpecLike.run$(AnyFlatSpecLike.scala:1943)
	at org.scalatest.flatspec.AnyFlatSpec.run(AnyFlatSpec.scala:1686)
	at org.scalatest.tools.Framework.org$scalatest$tools$Framework$$runSuite(Framework.scala:321)
	at org.scalatest.tools.Framework$ScalaTestTask.execute(Framework.scala:517)
	at sbt.TestRunner.runTest$1(TestFramework.scala:153)
	at sbt.TestRunner.run(TestFramework.scala:168)
	at sbt.TestFramework$$anon$3$$anonfun$$lessinit$greater$1.$anonfun$apply$1(TestFramework.scala:336)
	at sbt.TestFramework$.sbt$TestFramework$$withContextLoader(TestFramework.scala:296)
	at sbt.TestFramework$$anon$3$$anonfun$$lessinit$greater$1.apply(TestFramework.scala:336)
	at sbt.TestFramework$$anon$3$$anonfun$$lessinit$greater$1.apply(TestFramework.scala:336)
	at sbt.TestFunction.apply(TestFramework.scala:348)
	at sbt.Tests$.$anonfun$toTask$1(Tests.scala:436)
	at sbt.std.Transform$$anon$3.$anonfun$apply$2(Transform.scala:47)
	at sbt.std.Transform$$anon$4.work(Transform.scala:69)
	at sbt.Execute.$anonfun$submit$2(Execute.scala:283)
	at sbt.internal.util.ErrorHandling$.wideConvert(ErrorHandling.scala:24)
	at sbt.Execute.work(Execute.scala:292)
	at sbt.Execute.$anonfun$submit$1(Execute.scala:283)
	at sbt.ConcurrentRestrictions$$anon$4.$anonfun$submitValid$1(ConcurrentRestrictions.scala:265)
	at sbt.CompletionService$$anon$2.call(CompletionService.scala:65)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
	at java.base/java.lang.Thread.run(Thread.java:840)
org.apache.texera.amber.compiler.WorkflowCompilerSpec::WorkflowCompiler should accumulate, not throw, when a scan source has no fileName
Stack Traces | 0.017s run time
org.scalatest.exceptions.TestFailedException: "java.lang.RuntimeException: No file selected. Please select a file from the 'File' dropdown in the right panel." did not contain "no input file name" unexpected message: java.lang.RuntimeException: No file selected. Please select a file from the 'File' dropdown in the right panel.
	at org.scalatest.Assertions.newAssertionFailedException(Assertions.scala:472)
	at org.scalatest.Assertions.newAssertionFailedException$(Assertions.scala:471)
	at org.scalatest.Assertions$.newAssertionFailedException(Assertions.scala:1231)
	at org.scalatest.Assertions$AssertionsHelper.macroAssert(Assertions.scala:1295)
	at org.apache.texera.amber.compiler.WorkflowCompilerSpec.$anonfun$new$4(WorkflowCompilerSpec.scala:213)
	at org.scalatest.OutcomeOf.outcomeOf(OutcomeOf.scala:85)
	at org.scalatest.OutcomeOf.outcomeOf$(OutcomeOf.scala:83)
	at org.scalatest.OutcomeOf$.outcomeOf(OutcomeOf.scala:104)
	at org.scalatest.Transformer.apply(Transformer.scala:22)
	at org.scalatest.Transformer.apply(Transformer.scala:20)
	at org.scalatest.flatspec.AnyFlatSpecLike$$anon$5.apply(AnyFlatSpecLike.scala:1832)
	at org.scalatest.TestSuite.withFixture(TestSuite.scala:196)
	at org.scalatest.TestSuite.withFixture$(TestSuite.scala:195)
	at org.scalatest.flatspec.AnyFlatSpec.withFixture(AnyFlatSpec.scala:1686)
	at org.scalatest.flatspec.AnyFlatSpecLike.invokeWithFixture$1(AnyFlatSpecLike.scala:1830)
	at org.scalatest.flatspec.AnyFlatSpecLike.$anonfun$runTest$1(AnyFlatSpecLike.scala:1842)
	at org.scalatest.SuperEngine.runTestImpl(Engine.scala:306)
	at org.scalatest.flatspec.AnyFlatSpecLike.runTest(AnyFlatSpecLike.scala:1842)
	at org.scalatest.flatspec.AnyFlatSpecLike.runTest$(AnyFlatSpecLike.scala:1824)
	at org.scalatest.flatspec.AnyFlatSpec.runTest(AnyFlatSpec.scala:1686)
	at org.scalatest.flatspec.AnyFlatSpecLike.$anonfun$runTests$1(AnyFlatSpecLike.scala:1900)
	at org.scalatest.SuperEngine.$anonfun$runTestsInBranch$1(Engine.scala:413)
	at scala.collection.immutable.List.foreach(List.scala:323)
	at org.scalatest.SuperEngine.traverseSubNodes$1(Engine.scala:401)
	at org.scalatest.SuperEngine.runTestsInBranch(Engine.scala:390)
	at org.scalatest.SuperEngine.$anonfun$runTestsInBranch$1(Engine.scala:427)
	at scala.collection.immutable.List.foreach(List.scala:323)
	at org.scalatest.SuperEngine.traverseSubNodes$1(Engine.scala:401)
	at org.scalatest.SuperEngine.runTestsInBranch(Engine.scala:396)
	at org.scalatest.SuperEngine.runTestsImpl(Engine.scala:475)
	at org.scalatest.flatspec.AnyFlatSpecLike.runTests(AnyFlatSpecLike.scala:1900)
	at org.scalatest.flatspec.AnyFlatSpecLike.runTests$(AnyFlatSpecLike.scala:1899)
	at org.scalatest.flatspec.AnyFlatSpec.runTests(AnyFlatSpec.scala:1686)
	at org.scalatest.Suite.run(Suite.scala:1114)
	at org.scalatest.Suite.run$(Suite.scala:1096)
	at org.scalatest.flatspec.AnyFlatSpec.org$scalatest$flatspec$AnyFlatSpecLike$$super$run(AnyFlatSpec.scala:1686)
	at org.scalatest.flatspec.AnyFlatSpecLike.$anonfun$run$1(AnyFlatSpecLike.scala:1945)
	at org.scalatest.SuperEngine.runImpl(Engine.scala:535)
	at org.scalatest.flatspec.AnyFlatSpecLike.run(AnyFlatSpecLike.scala:1945)
	at org.scalatest.flatspec.AnyFlatSpecLike.run$(AnyFlatSpecLike.scala:1943)
	at org.scalatest.flatspec.AnyFlatSpec.run(AnyFlatSpec.scala:1686)
	at org.scalatest.tools.Framework.org$scalatest$tools$Framework$$runSuite(Framework.scala:321)
	at org.scalatest.tools.Framework$ScalaTestTask.execute(Framework.scala:517)
	at sbt.TestRunner.runTest$1(TestFramework.scala:153)
	at sbt.TestRunner.run(TestFramework.scala:168)
	at sbt.TestFramework$$anon$3$$anonfun$$lessinit$greater$1.$anonfun$apply$1(TestFramework.scala:336)
	at sbt.TestFramework$.sbt$TestFramework$$withContextLoader(TestFramework.scala:296)
	at sbt.TestFramework$$anon$3$$anonfun$$lessinit$greater$1.apply(TestFramework.scala:336)
	at sbt.TestFramework$$anon$3$$anonfun$$lessinit$greater$1.apply(TestFramework.scala:336)
	at sbt.TestFunction.apply(TestFramework.scala:348)
	at sbt.Tests$.$anonfun$toTask$1(Tests.scala:436)
	at sbt.std.Transform$$anon$3.$anonfun$apply$2(Transform.scala:47)
	at sbt.std.Transform$$anon$4.work(Transform.scala:69)
	at sbt.Execute.$anonfun$submit$2(Execute.scala:283)
	at sbt.internal.util.ErrorHandling$.wideConvert(ErrorHandling.scala:24)
	at sbt.Execute.work(Execute.scala:292)
	at sbt.Execute.$anonfun$submit$1(Execute.scala:283)
	at sbt.ConcurrentRestrictions$$anon$4.$anonfun$submitValid$1(ConcurrentRestrictions.scala:265)
	at sbt.CompletionService$$anon$2.call(CompletionService.scala:65)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
	at java.base/java.lang.Thread.run(Thread.java:840)

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

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

Labels

common engine frontend Changes related to the frontend GUI platform Non-amber Scala service paths

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants